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 "gpu/command_buffer/service/query_manager.h" 6 7 #include "base/atomicops.h" 8 #include "base/bind.h" 9 #include "base/logging.h" 10 #include "base/memory/shared_memory.h" 11 #include "base/synchronization/lock.h" 12 #include "base/time/time.h" 13 #include "gpu/command_buffer/common/gles2_cmd_format.h" 14 #include "gpu/command_buffer/service/async_pixel_transfer_manager.h" 15 #include "gpu/command_buffer/service/error_state.h" 16 #include "gpu/command_buffer/service/feature_info.h" 17 #include "gpu/command_buffer/service/gles2_cmd_decoder.h" 18 19 namespace gpu { 20 namespace gles2 { 21 22 namespace { 23 24 class AsyncPixelTransferCompletionObserverImpl 25 : public AsyncPixelTransferCompletionObserver { 26 public: 27 AsyncPixelTransferCompletionObserverImpl(uint32 submit_count) 28 : submit_count_(submit_count), 29 cancelled_(false) {} 30 31 void Cancel() { 32 base::AutoLock locked(lock_); 33 cancelled_ = true; 34 } 35 36 virtual void DidComplete(const AsyncMemoryParams& mem_params) OVERRIDE { 37 base::AutoLock locked(lock_); 38 if (!cancelled_) { 39 DCHECK(mem_params.shared_memory); 40 DCHECK(mem_params.shared_memory->memory()); 41 void* data = static_cast<int8*>(mem_params.shared_memory->memory()) + 42 mem_params.shm_data_offset; 43 QuerySync* sync = static_cast<QuerySync*>(data); 44 45 // Need a MemoryBarrier here to ensure that upload completed before 46 // submit_count was written to sync->process_count. 47 base::subtle::MemoryBarrier(); 48 sync->process_count = submit_count_; 49 } 50 } 51 52 private: 53 virtual ~AsyncPixelTransferCompletionObserverImpl() {} 54 55 uint32 submit_count_; 56 57 base::Lock lock_; 58 bool cancelled_; 59 60 DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferCompletionObserverImpl); 61 }; 62 63 class AsyncPixelTransfersCompletedQuery 64 : public QueryManager::Query, 65 public base::SupportsWeakPtr<AsyncPixelTransfersCompletedQuery> { 66 public: 67 AsyncPixelTransfersCompletedQuery( 68 QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset); 69 70 virtual bool Begin() OVERRIDE; 71 virtual bool End(uint32 submit_count) OVERRIDE; 72 virtual bool Process() OVERRIDE; 73 virtual void Destroy(bool have_context) OVERRIDE; 74 75 protected: 76 virtual ~AsyncPixelTransfersCompletedQuery(); 77 78 scoped_refptr<AsyncPixelTransferCompletionObserverImpl> observer_; 79 }; 80 81 AsyncPixelTransfersCompletedQuery::AsyncPixelTransfersCompletedQuery( 82 QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset) 83 : Query(manager, target, shm_id, shm_offset) { 84 } 85 86 bool AsyncPixelTransfersCompletedQuery::Begin() { 87 return true; 88 } 89 90 bool AsyncPixelTransfersCompletedQuery::End(uint32 submit_count) { 91 AsyncMemoryParams mem_params; 92 // Get the real shared memory since it might need to be duped to prevent 93 // use-after-free of the memory. 94 Buffer buffer = manager()->decoder()->GetSharedMemoryBuffer(shm_id()); 95 if (!buffer.shared_memory) 96 return false; 97 mem_params.shared_memory = buffer.shared_memory; 98 mem_params.shm_size = buffer.size; 99 mem_params.shm_data_offset = shm_offset(); 100 mem_params.shm_data_size = sizeof(QuerySync); 101 uint32 end = mem_params.shm_data_offset + mem_params.shm_data_size; 102 if (end > mem_params.shm_size || end < mem_params.shm_data_offset) 103 return false; 104 105 observer_ = new AsyncPixelTransferCompletionObserverImpl(submit_count); 106 107 // Ask AsyncPixelTransferDelegate to run completion callback after all 108 // previous async transfers are done. No guarantee that callback is run 109 // on the current thread. 110 manager()->decoder()->GetAsyncPixelTransferManager() 111 ->AsyncNotifyCompletion(mem_params, observer_); 112 113 return AddToPendingTransferQueue(submit_count); 114 } 115 116 bool AsyncPixelTransfersCompletedQuery::Process() { 117 QuerySync* sync = manager()->decoder()->GetSharedMemoryAs<QuerySync*>( 118 shm_id(), shm_offset(), sizeof(*sync)); 119 if (!sync) 120 return false; 121 122 // Check if completion callback has been run. sync->process_count atomicity 123 // is guaranteed as this is already used to notify client of a completed 124 // query. 125 if (sync->process_count != submit_count()) 126 return true; 127 128 UnmarkAsPending(); 129 return true; 130 } 131 132 void AsyncPixelTransfersCompletedQuery::Destroy(bool /* have_context */) { 133 if (!IsDeleted()) { 134 MarkAsDeleted(); 135 } 136 } 137 138 AsyncPixelTransfersCompletedQuery::~AsyncPixelTransfersCompletedQuery() { 139 if (observer_) 140 observer_->Cancel(); 141 } 142 143 } // namespace 144 145 class AllSamplesPassedQuery : public QueryManager::Query { 146 public: 147 AllSamplesPassedQuery( 148 QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset, 149 GLuint service_id); 150 virtual bool Begin() OVERRIDE; 151 virtual bool End(uint32 submit_count) OVERRIDE; 152 virtual bool Process() OVERRIDE; 153 virtual void Destroy(bool have_context) OVERRIDE; 154 155 protected: 156 virtual ~AllSamplesPassedQuery(); 157 158 private: 159 // Service side query id. 160 GLuint service_id_; 161 }; 162 163 AllSamplesPassedQuery::AllSamplesPassedQuery( 164 QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset, 165 GLuint service_id) 166 : Query(manager, target, shm_id, shm_offset), 167 service_id_(service_id) { 168 } 169 170 bool AllSamplesPassedQuery::Begin() { 171 BeginQueryHelper(target(), service_id_); 172 return true; 173 } 174 175 bool AllSamplesPassedQuery::End(uint32 submit_count) { 176 EndQueryHelper(target()); 177 return AddToPendingQueue(submit_count); 178 } 179 180 bool AllSamplesPassedQuery::Process() { 181 GLuint available = 0; 182 glGetQueryObjectuivARB( 183 service_id_, GL_QUERY_RESULT_AVAILABLE_EXT, &available); 184 if (!available) { 185 return true; 186 } 187 GLuint result = 0; 188 glGetQueryObjectuivARB( 189 service_id_, GL_QUERY_RESULT_EXT, &result); 190 191 return MarkAsCompleted(result != 0); 192 } 193 194 void AllSamplesPassedQuery::Destroy(bool have_context) { 195 if (have_context && !IsDeleted()) { 196 glDeleteQueriesARB(1, &service_id_); 197 MarkAsDeleted(); 198 } 199 } 200 201 AllSamplesPassedQuery::~AllSamplesPassedQuery() { 202 } 203 204 class CommandsIssuedQuery : public QueryManager::Query { 205 public: 206 CommandsIssuedQuery( 207 QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset); 208 209 virtual bool Begin() OVERRIDE; 210 virtual bool End(uint32 submit_count) OVERRIDE; 211 virtual bool Process() OVERRIDE; 212 virtual void Destroy(bool have_context) OVERRIDE; 213 214 protected: 215 virtual ~CommandsIssuedQuery(); 216 217 private: 218 base::TimeTicks begin_time_; 219 }; 220 221 CommandsIssuedQuery::CommandsIssuedQuery( 222 QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset) 223 : Query(manager, target, shm_id, shm_offset) { 224 } 225 226 bool CommandsIssuedQuery::Begin() { 227 begin_time_ = base::TimeTicks::HighResNow(); 228 return true; 229 } 230 231 bool CommandsIssuedQuery::End(uint32 submit_count) { 232 base::TimeDelta elapsed = base::TimeTicks::HighResNow() - begin_time_; 233 MarkAsPending(submit_count); 234 return MarkAsCompleted(elapsed.InMicroseconds()); 235 } 236 237 bool CommandsIssuedQuery::Process() { 238 NOTREACHED(); 239 return true; 240 } 241 242 void CommandsIssuedQuery::Destroy(bool /* have_context */) { 243 if (!IsDeleted()) { 244 MarkAsDeleted(); 245 } 246 } 247 248 CommandsIssuedQuery::~CommandsIssuedQuery() { 249 } 250 251 class CommandLatencyQuery : public QueryManager::Query { 252 public: 253 CommandLatencyQuery( 254 QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset); 255 256 virtual bool Begin() OVERRIDE; 257 virtual bool End(uint32 submit_count) OVERRIDE; 258 virtual bool Process() OVERRIDE; 259 virtual void Destroy(bool have_context) OVERRIDE; 260 261 protected: 262 virtual ~CommandLatencyQuery(); 263 }; 264 265 CommandLatencyQuery::CommandLatencyQuery( 266 QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset) 267 : Query(manager, target, shm_id, shm_offset) { 268 } 269 270 bool CommandLatencyQuery::Begin() { 271 return true; 272 } 273 274 bool CommandLatencyQuery::End(uint32 submit_count) { 275 base::TimeDelta now = base::TimeTicks::HighResNow() - base::TimeTicks(); 276 MarkAsPending(submit_count); 277 return MarkAsCompleted(now.InMicroseconds()); 278 } 279 280 bool CommandLatencyQuery::Process() { 281 NOTREACHED(); 282 return true; 283 } 284 285 void CommandLatencyQuery::Destroy(bool /* have_context */) { 286 if (!IsDeleted()) { 287 MarkAsDeleted(); 288 } 289 } 290 291 CommandLatencyQuery::~CommandLatencyQuery() { 292 } 293 294 295 class AsyncReadPixelsCompletedQuery 296 : public QueryManager::Query, 297 public base::SupportsWeakPtr<AsyncReadPixelsCompletedQuery> { 298 public: 299 AsyncReadPixelsCompletedQuery( 300 QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset); 301 302 virtual bool Begin() OVERRIDE; 303 virtual bool End(uint32 submit_count) OVERRIDE; 304 virtual bool Process() OVERRIDE; 305 virtual void Destroy(bool have_context) OVERRIDE; 306 307 protected: 308 void Complete(); 309 virtual ~AsyncReadPixelsCompletedQuery(); 310 }; 311 312 AsyncReadPixelsCompletedQuery::AsyncReadPixelsCompletedQuery( 313 QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset) 314 : Query(manager, target, shm_id, shm_offset) { 315 } 316 317 bool AsyncReadPixelsCompletedQuery::Begin() { 318 return true; 319 } 320 321 bool AsyncReadPixelsCompletedQuery::End(uint32 submit_count) { 322 if (!AddToPendingQueue(submit_count)) { 323 return false; 324 } 325 manager()->decoder()->WaitForReadPixels( 326 base::Bind(&AsyncReadPixelsCompletedQuery::Complete, 327 AsWeakPtr())); 328 329 return true; 330 } 331 332 void AsyncReadPixelsCompletedQuery::Complete() { 333 MarkAsCompleted(1); 334 } 335 336 bool AsyncReadPixelsCompletedQuery::Process() { 337 return true; 338 } 339 340 void AsyncReadPixelsCompletedQuery::Destroy(bool /* have_context */) { 341 if (!IsDeleted()) { 342 MarkAsDeleted(); 343 } 344 } 345 346 AsyncReadPixelsCompletedQuery::~AsyncReadPixelsCompletedQuery() { 347 } 348 349 350 class GetErrorQuery : public QueryManager::Query { 351 public: 352 GetErrorQuery( 353 QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset); 354 355 virtual bool Begin() OVERRIDE; 356 virtual bool End(uint32 submit_count) OVERRIDE; 357 virtual bool Process() OVERRIDE; 358 virtual void Destroy(bool have_context) OVERRIDE; 359 360 protected: 361 virtual ~GetErrorQuery(); 362 363 private: 364 }; 365 366 GetErrorQuery::GetErrorQuery( 367 QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset) 368 : Query(manager, target, shm_id, shm_offset) { 369 } 370 371 bool GetErrorQuery::Begin() { 372 return true; 373 } 374 375 bool GetErrorQuery::End(uint32 submit_count) { 376 MarkAsPending(submit_count); 377 return MarkAsCompleted(manager()->decoder()->GetErrorState()->GetGLError()); 378 } 379 380 bool GetErrorQuery::Process() { 381 NOTREACHED(); 382 return true; 383 } 384 385 void GetErrorQuery::Destroy(bool /* have_context */) { 386 if (!IsDeleted()) { 387 MarkAsDeleted(); 388 } 389 } 390 391 GetErrorQuery::~GetErrorQuery() { 392 } 393 394 QueryManager::QueryManager( 395 GLES2Decoder* decoder, 396 FeatureInfo* feature_info) 397 : decoder_(decoder), 398 use_arb_occlusion_query2_for_occlusion_query_boolean_( 399 feature_info->feature_flags( 400 ).use_arb_occlusion_query2_for_occlusion_query_boolean), 401 use_arb_occlusion_query_for_occlusion_query_boolean_( 402 feature_info->feature_flags( 403 ).use_arb_occlusion_query_for_occlusion_query_boolean), 404 query_count_(0) { 405 DCHECK(!(use_arb_occlusion_query_for_occlusion_query_boolean_ && 406 use_arb_occlusion_query2_for_occlusion_query_boolean_)); 407 } 408 409 QueryManager::~QueryManager() { 410 DCHECK(queries_.empty()); 411 412 // If this triggers, that means something is keeping a reference to 413 // a Query belonging to this. 414 CHECK_EQ(query_count_, 0u); 415 } 416 417 void QueryManager::Destroy(bool have_context) { 418 pending_queries_.clear(); 419 pending_transfer_queries_.clear(); 420 while (!queries_.empty()) { 421 Query* query = queries_.begin()->second.get(); 422 query->Destroy(have_context); 423 queries_.erase(queries_.begin()); 424 } 425 } 426 427 QueryManager::Query* QueryManager::CreateQuery( 428 GLenum target, GLuint client_id, int32 shm_id, uint32 shm_offset) { 429 scoped_refptr<Query> query; 430 switch (target) { 431 case GL_COMMANDS_ISSUED_CHROMIUM: 432 query = new CommandsIssuedQuery(this, target, shm_id, shm_offset); 433 break; 434 case GL_LATENCY_QUERY_CHROMIUM: 435 query = new CommandLatencyQuery(this, target, shm_id, shm_offset); 436 break; 437 case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM: 438 // Currently async pixel transfer delegates only support uploads. 439 query = new AsyncPixelTransfersCompletedQuery( 440 this, target, shm_id, shm_offset); 441 break; 442 case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM: 443 query = new AsyncReadPixelsCompletedQuery( 444 this, target, shm_id, shm_offset); 445 break; 446 case GL_GET_ERROR_QUERY_CHROMIUM: 447 query = new GetErrorQuery(this, target, shm_id, shm_offset); 448 break; 449 default: { 450 GLuint service_id = 0; 451 glGenQueriesARB(1, &service_id); 452 DCHECK_NE(0u, service_id); 453 query = new AllSamplesPassedQuery( 454 this, target, shm_id, shm_offset, service_id); 455 break; 456 } 457 } 458 std::pair<QueryMap::iterator, bool> result = 459 queries_.insert(std::make_pair(client_id, query)); 460 DCHECK(result.second); 461 return query.get(); 462 } 463 464 QueryManager::Query* QueryManager::GetQuery( 465 GLuint client_id) { 466 QueryMap::iterator it = queries_.find(client_id); 467 return it != queries_.end() ? it->second.get() : NULL; 468 } 469 470 void QueryManager::RemoveQuery(GLuint client_id) { 471 QueryMap::iterator it = queries_.find(client_id); 472 if (it != queries_.end()) { 473 Query* query = it->second.get(); 474 RemovePendingQuery(query); 475 query->MarkAsDeleted(); 476 queries_.erase(it); 477 } 478 } 479 480 void QueryManager::StartTracking(QueryManager::Query* /* query */) { 481 ++query_count_; 482 } 483 484 void QueryManager::StopTracking(QueryManager::Query* /* query */) { 485 --query_count_; 486 } 487 488 GLenum QueryManager::AdjustTargetForEmulation(GLenum target) { 489 switch (target) { 490 case GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT: 491 case GL_ANY_SAMPLES_PASSED_EXT: 492 if (use_arb_occlusion_query2_for_occlusion_query_boolean_) { 493 // ARB_occlusion_query2 does not have a 494 // GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT 495 // target. 496 target = GL_ANY_SAMPLES_PASSED_EXT; 497 } else if (use_arb_occlusion_query_for_occlusion_query_boolean_) { 498 // ARB_occlusion_query does not have a 499 // GL_ANY_SAMPLES_PASSED_EXT 500 // target. 501 target = GL_SAMPLES_PASSED_ARB; 502 } 503 break; 504 default: 505 break; 506 } 507 return target; 508 } 509 510 void QueryManager::BeginQueryHelper(GLenum target, GLuint id) { 511 target = AdjustTargetForEmulation(target); 512 glBeginQueryARB(target, id); 513 } 514 515 void QueryManager::EndQueryHelper(GLenum target) { 516 target = AdjustTargetForEmulation(target); 517 glEndQueryARB(target); 518 } 519 520 QueryManager::Query::Query( 521 QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset) 522 : manager_(manager), 523 target_(target), 524 shm_id_(shm_id), 525 shm_offset_(shm_offset), 526 submit_count_(0), 527 pending_(false), 528 deleted_(false) { 529 DCHECK(manager); 530 manager_->StartTracking(this); 531 } 532 533 void QueryManager::Query::RunCallbacks() { 534 for (size_t i = 0; i < callbacks_.size(); i++) { 535 callbacks_[i].Run(); 536 } 537 callbacks_.clear(); 538 } 539 540 void QueryManager::Query::AddCallback(base::Closure callback) { 541 if (pending_) { 542 callbacks_.push_back(callback); 543 } else { 544 callback.Run(); 545 } 546 } 547 548 QueryManager::Query::~Query() { 549 // The query is getting deleted, either by the client or 550 // because the context was lost. Call any outstanding 551 // callbacks to avoid leaks. 552 RunCallbacks(); 553 if (manager_) { 554 manager_->StopTracking(this); 555 manager_ = NULL; 556 } 557 } 558 559 bool QueryManager::Query::MarkAsCompleted(uint64 result) { 560 DCHECK(pending_); 561 QuerySync* sync = manager_->decoder_->GetSharedMemoryAs<QuerySync*>( 562 shm_id_, shm_offset_, sizeof(*sync)); 563 if (!sync) { 564 return false; 565 } 566 567 pending_ = false; 568 sync->result = result; 569 // Need a MemoryBarrier here so that sync->result is written before 570 // sync->process_count. 571 base::subtle::MemoryBarrier(); 572 sync->process_count = submit_count_; 573 574 return true; 575 } 576 577 bool QueryManager::ProcessPendingQueries() { 578 while (!pending_queries_.empty()) { 579 Query* query = pending_queries_.front().get(); 580 if (!query->Process()) { 581 return false; 582 } 583 if (query->pending()) { 584 break; 585 } 586 query->RunCallbacks(); 587 pending_queries_.pop_front(); 588 } 589 590 return true; 591 } 592 593 bool QueryManager::HavePendingQueries() { 594 return !pending_queries_.empty(); 595 } 596 597 bool QueryManager::ProcessPendingTransferQueries() { 598 while (!pending_transfer_queries_.empty()) { 599 Query* query = pending_transfer_queries_.front().get(); 600 if (!query->Process()) { 601 return false; 602 } 603 if (query->pending()) { 604 break; 605 } 606 query->RunCallbacks(); 607 pending_transfer_queries_.pop_front(); 608 } 609 610 return true; 611 } 612 613 bool QueryManager::HavePendingTransferQueries() { 614 return !pending_transfer_queries_.empty(); 615 } 616 617 bool QueryManager::AddPendingQuery(Query* query, uint32 submit_count) { 618 DCHECK(query); 619 DCHECK(!query->IsDeleted()); 620 if (!RemovePendingQuery(query)) { 621 return false; 622 } 623 query->MarkAsPending(submit_count); 624 pending_queries_.push_back(query); 625 return true; 626 } 627 628 bool QueryManager::AddPendingTransferQuery(Query* query, uint32 submit_count) { 629 DCHECK(query); 630 DCHECK(!query->IsDeleted()); 631 if (!RemovePendingQuery(query)) { 632 return false; 633 } 634 query->MarkAsPending(submit_count); 635 pending_transfer_queries_.push_back(query); 636 return true; 637 } 638 639 bool QueryManager::RemovePendingQuery(Query* query) { 640 DCHECK(query); 641 if (query->pending()) { 642 // TODO(gman): Speed this up if this is a common operation. This would only 643 // happen if you do being/end begin/end on the same query without waiting 644 // for the first one to finish. 645 for (QueryQueue::iterator it = pending_queries_.begin(); 646 it != pending_queries_.end(); ++it) { 647 if (it->get() == query) { 648 pending_queries_.erase(it); 649 break; 650 } 651 } 652 for (QueryQueue::iterator it = pending_transfer_queries_.begin(); 653 it != pending_transfer_queries_.end(); ++it) { 654 if (it->get() == query) { 655 pending_transfer_queries_.erase(it); 656 break; 657 } 658 } 659 if (!query->MarkAsCompleted(0)) { 660 return false; 661 } 662 } 663 return true; 664 } 665 666 bool QueryManager::BeginQuery(Query* query) { 667 DCHECK(query); 668 if (!RemovePendingQuery(query)) { 669 return false; 670 } 671 return query->Begin(); 672 } 673 674 bool QueryManager::EndQuery(Query* query, uint32 submit_count) { 675 DCHECK(query); 676 if (!RemovePendingQuery(query)) { 677 return false; 678 } 679 return query->End(submit_count); 680 } 681 682 } // namespace gles2 683 } // namespace gpu 684