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 "gpu/command_buffer/service/query_manager.h"
7 #include "base/atomicops.h"
9 #include "base/logging.h"
10 #include "base/memory/shared_memory.h"
11 #include "base/numerics/safe_math.h"
12 #include "base/synchronization/lock.h"
13 #include "base/time/time.h"
14 #include "gpu/command_buffer/common/gles2_cmd_format.h"
15 #include "gpu/command_buffer/service/async_pixel_transfer_manager.h"
16 #include "gpu/command_buffer/service/error_state.h"
17 #include "gpu/command_buffer/service/feature_info.h"
18 #include "gpu/command_buffer/service/gles2_cmd_decoder.h"
19 #include "ui/gl/gl_fence.h"
26 class AsyncPixelTransferCompletionObserverImpl
27 : public AsyncPixelTransferCompletionObserver
{
29 AsyncPixelTransferCompletionObserverImpl(base::subtle::Atomic32 submit_count
)
30 : submit_count_(submit_count
), cancelled_(false) {}
33 base::AutoLock
locked(lock_
);
37 void DidComplete(const AsyncMemoryParams
& mem_params
) override
{
38 base::AutoLock
locked(lock_
);
40 DCHECK(mem_params
.buffer().get());
41 void* data
= mem_params
.GetDataAddress();
42 QuerySync
* sync
= static_cast<QuerySync
*>(data
);
43 base::subtle::Release_Store(&sync
->process_count
, submit_count_
);
48 ~AsyncPixelTransferCompletionObserverImpl() override
{}
50 base::subtle::Atomic32 submit_count_
;
55 DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferCompletionObserverImpl
);
58 class AsyncPixelTransfersCompletedQuery
59 : public QueryManager::Query
,
60 public base::SupportsWeakPtr
<AsyncPixelTransfersCompletedQuery
> {
62 AsyncPixelTransfersCompletedQuery(
63 QueryManager
* manager
, GLenum target
, int32 shm_id
, uint32 shm_offset
);
65 bool Begin() override
;
66 bool End(base::subtle::Atomic32 submit_count
) override
;
67 bool Process(bool did_finish
) override
;
68 void Destroy(bool have_context
) override
;
71 ~AsyncPixelTransfersCompletedQuery() override
;
73 scoped_refptr
<AsyncPixelTransferCompletionObserverImpl
> observer_
;
76 AsyncPixelTransfersCompletedQuery::AsyncPixelTransfersCompletedQuery(
77 QueryManager
* manager
, GLenum target
, int32 shm_id
, uint32 shm_offset
)
78 : Query(manager
, target
, shm_id
, shm_offset
) {
81 bool AsyncPixelTransfersCompletedQuery::Begin() {
85 bool AsyncPixelTransfersCompletedQuery::End(
86 base::subtle::Atomic32 submit_count
) {
87 // Get the real shared memory since it might need to be duped to prevent
88 // use-after-free of the memory.
89 scoped_refptr
<Buffer
> buffer
=
90 manager()->decoder()->GetSharedMemoryBuffer(shm_id());
93 AsyncMemoryParams
mem_params(buffer
, shm_offset(), sizeof(QuerySync
));
94 if (!mem_params
.GetDataAddress())
97 observer_
= new AsyncPixelTransferCompletionObserverImpl(submit_count
);
99 // Ask AsyncPixelTransferDelegate to run completion callback after all
100 // previous async transfers are done. No guarantee that callback is run
101 // on the current thread.
102 manager()->decoder()->GetAsyncPixelTransferManager()->AsyncNotifyCompletion(
103 mem_params
, observer_
.get());
105 return AddToPendingTransferQueue(submit_count
);
108 bool AsyncPixelTransfersCompletedQuery::Process(bool did_finish
) {
109 QuerySync
* sync
= manager()->decoder()->GetSharedMemoryAs
<QuerySync
*>(
110 shm_id(), shm_offset(), sizeof(*sync
));
114 // Check if completion callback has been run. sync->process_count atomicity
115 // is guaranteed as this is already used to notify client of a completed
117 if (base::subtle::Acquire_Load(&sync
->process_count
) != submit_count())
124 void AsyncPixelTransfersCompletedQuery::Destroy(bool /* have_context */) {
130 AsyncPixelTransfersCompletedQuery::~AsyncPixelTransfersCompletedQuery() {
137 class AllSamplesPassedQuery
: public QueryManager::Query
{
139 AllSamplesPassedQuery(
140 QueryManager
* manager
, GLenum target
, int32 shm_id
, uint32 shm_offset
,
142 bool Begin() override
;
143 bool End(base::subtle::Atomic32 submit_count
) override
;
144 bool Process(bool did_finish
) override
;
145 void Destroy(bool have_context
) override
;
148 ~AllSamplesPassedQuery() override
;
151 // Service side query id.
155 AllSamplesPassedQuery::AllSamplesPassedQuery(
156 QueryManager
* manager
, GLenum target
, int32 shm_id
, uint32 shm_offset
,
158 : Query(manager
, target
, shm_id
, shm_offset
),
159 service_id_(service_id
) {
162 bool AllSamplesPassedQuery::Begin() {
163 BeginQueryHelper(target(), service_id_
);
167 bool AllSamplesPassedQuery::End(base::subtle::Atomic32 submit_count
) {
168 EndQueryHelper(target());
169 return AddToPendingQueue(submit_count
);
172 bool AllSamplesPassedQuery::Process(bool did_finish
) {
173 GLuint available
= 0;
175 service_id_
, GL_QUERY_RESULT_AVAILABLE_EXT
, &available
);
181 service_id_
, GL_QUERY_RESULT_EXT
, &result
);
183 return MarkAsCompleted(result
!= 0);
186 void AllSamplesPassedQuery::Destroy(bool have_context
) {
187 if (have_context
&& !IsDeleted()) {
188 glDeleteQueries(1, &service_id_
);
193 AllSamplesPassedQuery::~AllSamplesPassedQuery() {
196 class CommandsIssuedQuery
: public QueryManager::Query
{
199 QueryManager
* manager
, GLenum target
, int32 shm_id
, uint32 shm_offset
);
201 bool Begin() override
;
202 bool End(base::subtle::Atomic32 submit_count
) override
;
203 bool Process(bool did_finish
) override
;
204 void Destroy(bool have_context
) override
;
207 ~CommandsIssuedQuery() override
;
210 base::TimeTicks begin_time_
;
213 CommandsIssuedQuery::CommandsIssuedQuery(
214 QueryManager
* manager
, GLenum target
, int32 shm_id
, uint32 shm_offset
)
215 : Query(manager
, target
, shm_id
, shm_offset
) {
218 bool CommandsIssuedQuery::Begin() {
219 begin_time_
= base::TimeTicks::Now();
223 bool CommandsIssuedQuery::End(base::subtle::Atomic32 submit_count
) {
224 base::TimeDelta elapsed
= base::TimeTicks::Now() - begin_time_
;
225 MarkAsPending(submit_count
);
226 return MarkAsCompleted(elapsed
.InMicroseconds());
229 bool CommandsIssuedQuery::Process(bool did_finish
) {
234 void CommandsIssuedQuery::Destroy(bool /* have_context */) {
240 CommandsIssuedQuery::~CommandsIssuedQuery() {
243 class CommandLatencyQuery
: public QueryManager::Query
{
246 QueryManager
* manager
, GLenum target
, int32 shm_id
, uint32 shm_offset
);
248 bool Begin() override
;
249 bool End(base::subtle::Atomic32 submit_count
) override
;
250 bool Process(bool did_finish
) override
;
251 void Destroy(bool have_context
) override
;
254 ~CommandLatencyQuery() override
;
257 CommandLatencyQuery::CommandLatencyQuery(
258 QueryManager
* manager
, GLenum target
, int32 shm_id
, uint32 shm_offset
)
259 : Query(manager
, target
, shm_id
, shm_offset
) {
262 bool CommandLatencyQuery::Begin() {
266 bool CommandLatencyQuery::End(base::subtle::Atomic32 submit_count
) {
267 base::TimeDelta now
= base::TimeTicks::Now() - base::TimeTicks();
268 MarkAsPending(submit_count
);
269 return MarkAsCompleted(now
.InMicroseconds());
272 bool CommandLatencyQuery::Process(bool did_finish
) {
277 void CommandLatencyQuery::Destroy(bool /* have_context */) {
283 CommandLatencyQuery::~CommandLatencyQuery() {
287 class AsyncReadPixelsCompletedQuery
288 : public QueryManager::Query
,
289 public base::SupportsWeakPtr
<AsyncReadPixelsCompletedQuery
> {
291 AsyncReadPixelsCompletedQuery(
292 QueryManager
* manager
, GLenum target
, int32 shm_id
, uint32 shm_offset
);
294 bool Begin() override
;
295 bool End(base::subtle::Atomic32 submit_count
) override
;
296 bool Process(bool did_finish
) override
;
297 void Destroy(bool have_context
) override
;
301 ~AsyncReadPixelsCompletedQuery() override
;
305 bool complete_result_
;
308 AsyncReadPixelsCompletedQuery::AsyncReadPixelsCompletedQuery(
309 QueryManager
* manager
, GLenum target
, int32 shm_id
, uint32 shm_offset
)
310 : Query(manager
, target
, shm_id
, shm_offset
),
312 complete_result_(false) {
315 bool AsyncReadPixelsCompletedQuery::Begin() {
319 bool AsyncReadPixelsCompletedQuery::End(base::subtle::Atomic32 submit_count
) {
320 if (!AddToPendingQueue(submit_count
)) {
323 manager()->decoder()->WaitForReadPixels(
324 base::Bind(&AsyncReadPixelsCompletedQuery::Complete
,
327 return Process(false);
330 void AsyncReadPixelsCompletedQuery::Complete() {
332 complete_result_
= MarkAsCompleted(1);
335 bool AsyncReadPixelsCompletedQuery::Process(bool did_finish
) {
336 return !completed_
|| complete_result_
;
339 void AsyncReadPixelsCompletedQuery::Destroy(bool /* have_context */) {
345 AsyncReadPixelsCompletedQuery::~AsyncReadPixelsCompletedQuery() {
349 class GetErrorQuery
: public QueryManager::Query
{
352 QueryManager
* manager
, GLenum target
, int32 shm_id
, uint32 shm_offset
);
354 bool Begin() override
;
355 bool End(base::subtle::Atomic32 submit_count
) override
;
356 bool Process(bool did_finish
) override
;
357 void Destroy(bool have_context
) override
;
360 ~GetErrorQuery() override
;
365 GetErrorQuery::GetErrorQuery(
366 QueryManager
* manager
, GLenum target
, int32 shm_id
, uint32 shm_offset
)
367 : Query(manager
, target
, shm_id
, shm_offset
) {
370 bool GetErrorQuery::Begin() {
374 bool GetErrorQuery::End(base::subtle::Atomic32 submit_count
) {
375 MarkAsPending(submit_count
);
376 return MarkAsCompleted(manager()->decoder()->GetErrorState()->GetGLError());
379 bool GetErrorQuery::Process(bool did_finish
) {
384 void GetErrorQuery::Destroy(bool /* have_context */) {
390 GetErrorQuery::~GetErrorQuery() {
393 class CommandsCompletedQuery
: public QueryManager::Query
{
395 CommandsCompletedQuery(QueryManager
* manager
,
400 // Overridden from QueryManager::Query:
401 bool Begin() override
;
402 bool End(base::subtle::Atomic32 submit_count
) override
;
403 bool Process(bool did_finish
) override
;
404 void Destroy(bool have_context
) override
;
407 ~CommandsCompletedQuery() override
;
410 scoped_ptr
<gfx::GLFence
> fence_
;
411 base::TimeTicks begin_time_
;
414 CommandsCompletedQuery::CommandsCompletedQuery(QueryManager
* manager
,
418 : Query(manager
, target
, shm_id
, shm_offset
) {}
420 bool CommandsCompletedQuery::Begin() {
421 begin_time_
= base::TimeTicks::Now();
425 bool CommandsCompletedQuery::End(base::subtle::Atomic32 submit_count
) {
426 fence_
.reset(gfx::GLFence::Create());
428 return AddToPendingQueue(submit_count
);
431 bool CommandsCompletedQuery::Process(bool did_finish
) {
432 // Note: |did_finish| guarantees that the GPU has passed the fence but
433 // we cannot assume that GLFence::HasCompleted() will return true yet as
434 // that's not guaranteed by all GLFence implementations.
435 if (!did_finish
&& fence_
&& !fence_
->HasCompleted())
438 base::TimeDelta elapsed
= base::TimeTicks::Now() - begin_time_
;
439 return MarkAsCompleted(elapsed
.InMicroseconds());
442 void CommandsCompletedQuery::Destroy(bool have_context
) {
443 if (have_context
&& !IsDeleted()) {
449 CommandsCompletedQuery::~CommandsCompletedQuery() {}
451 QueryManager::QueryManager(
452 GLES2Decoder
* decoder
,
453 FeatureInfo
* feature_info
)
455 use_arb_occlusion_query2_for_occlusion_query_boolean_(
456 feature_info
->feature_flags(
457 ).use_arb_occlusion_query2_for_occlusion_query_boolean
),
458 use_arb_occlusion_query_for_occlusion_query_boolean_(
459 feature_info
->feature_flags(
460 ).use_arb_occlusion_query_for_occlusion_query_boolean
),
462 DCHECK(!(use_arb_occlusion_query_for_occlusion_query_boolean_
&&
463 use_arb_occlusion_query2_for_occlusion_query_boolean_
));
466 QueryManager::~QueryManager() {
467 DCHECK(queries_
.empty());
469 // If this triggers, that means something is keeping a reference to
470 // a Query belonging to this.
471 CHECK_EQ(query_count_
, 0u);
474 void QueryManager::Destroy(bool have_context
) {
475 pending_queries_
.clear();
476 pending_transfer_queries_
.clear();
477 while (!queries_
.empty()) {
478 Query
* query
= queries_
.begin()->second
.get();
479 query
->Destroy(have_context
);
480 queries_
.erase(queries_
.begin());
484 QueryManager::Query
* QueryManager::CreateQuery(
485 GLenum target
, GLuint client_id
, int32 shm_id
, uint32 shm_offset
) {
486 scoped_refptr
<Query
> query
;
488 case GL_COMMANDS_ISSUED_CHROMIUM
:
489 query
= new CommandsIssuedQuery(this, target
, shm_id
, shm_offset
);
491 case GL_LATENCY_QUERY_CHROMIUM
:
492 query
= new CommandLatencyQuery(this, target
, shm_id
, shm_offset
);
494 case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM
:
495 // Currently async pixel transfer delegates only support uploads.
496 query
= new AsyncPixelTransfersCompletedQuery(
497 this, target
, shm_id
, shm_offset
);
499 case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM
:
500 query
= new AsyncReadPixelsCompletedQuery(
501 this, target
, shm_id
, shm_offset
);
503 case GL_GET_ERROR_QUERY_CHROMIUM
:
504 query
= new GetErrorQuery(this, target
, shm_id
, shm_offset
);
506 case GL_COMMANDS_COMPLETED_CHROMIUM
:
507 query
= new CommandsCompletedQuery(this, target
, shm_id
, shm_offset
);
510 GLuint service_id
= 0;
511 glGenQueries(1, &service_id
);
512 DCHECK_NE(0u, service_id
);
513 query
= new AllSamplesPassedQuery(
514 this, target
, shm_id
, shm_offset
, service_id
);
518 std::pair
<QueryMap::iterator
, bool> result
=
519 queries_
.insert(std::make_pair(client_id
, query
));
520 DCHECK(result
.second
);
524 void QueryManager::GenQueries(GLsizei n
, const GLuint
* queries
) {
526 for (GLsizei i
= 0; i
< n
; ++i
) {
527 generated_query_ids_
.insert(queries
[i
]);
531 bool QueryManager::IsValidQuery(GLuint id
) {
532 GeneratedQueryIds::iterator it
= generated_query_ids_
.find(id
);
533 return it
!= generated_query_ids_
.end();
536 QueryManager::Query
* QueryManager::GetQuery(
538 QueryMap::iterator it
= queries_
.find(client_id
);
539 return it
!= queries_
.end() ? it
->second
.get() : NULL
;
542 void QueryManager::RemoveQuery(GLuint client_id
) {
543 QueryMap::iterator it
= queries_
.find(client_id
);
544 if (it
!= queries_
.end()) {
545 Query
* query
= it
->second
.get();
546 RemovePendingQuery(query
);
547 query
->MarkAsDeleted();
550 generated_query_ids_
.erase(client_id
);
553 void QueryManager::StartTracking(QueryManager::Query
* /* query */) {
557 void QueryManager::StopTracking(QueryManager::Query
* /* query */) {
561 GLenum
QueryManager::AdjustTargetForEmulation(GLenum target
) {
563 case GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT
:
564 case GL_ANY_SAMPLES_PASSED_EXT
:
565 if (use_arb_occlusion_query2_for_occlusion_query_boolean_
) {
566 // ARB_occlusion_query2 does not have a
567 // GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT
569 target
= GL_ANY_SAMPLES_PASSED_EXT
;
570 } else if (use_arb_occlusion_query_for_occlusion_query_boolean_
) {
571 // ARB_occlusion_query does not have a
572 // GL_ANY_SAMPLES_PASSED_EXT
574 target
= GL_SAMPLES_PASSED_ARB
;
583 void QueryManager::BeginQueryHelper(GLenum target
, GLuint id
) {
584 target
= AdjustTargetForEmulation(target
);
585 glBeginQuery(target
, id
);
588 void QueryManager::EndQueryHelper(GLenum target
) {
589 target
= AdjustTargetForEmulation(target
);
593 QueryManager::Query::Query(
594 QueryManager
* manager
, GLenum target
, int32 shm_id
, uint32 shm_offset
)
598 shm_offset_(shm_offset
),
603 manager_
->StartTracking(this);
606 void QueryManager::Query::RunCallbacks() {
607 for (size_t i
= 0; i
< callbacks_
.size(); i
++) {
613 void QueryManager::Query::AddCallback(base::Closure callback
) {
615 callbacks_
.push_back(callback
);
621 QueryManager::Query::~Query() {
622 // The query is getting deleted, either by the client or
623 // because the context was lost. Call any outstanding
624 // callbacks to avoid leaks.
627 manager_
->StopTracking(this);
632 bool QueryManager::Query::MarkAsCompleted(uint64 result
) {
634 QuerySync
* sync
= manager_
->decoder_
->GetSharedMemoryAs
<QuerySync
*>(
635 shm_id_
, shm_offset_
, sizeof(*sync
));
641 sync
->result
= result
;
642 base::subtle::Release_Store(&sync
->process_count
, submit_count_
);
647 bool QueryManager::ProcessPendingQueries(bool did_finish
) {
648 while (!pending_queries_
.empty()) {
649 Query
* query
= pending_queries_
.front().get();
650 if (!query
->Process(did_finish
)) {
653 if (query
->pending()) {
656 query
->RunCallbacks();
657 pending_queries_
.pop_front();
663 bool QueryManager::HavePendingQueries() {
664 return !pending_queries_
.empty();
667 bool QueryManager::ProcessPendingTransferQueries() {
668 while (!pending_transfer_queries_
.empty()) {
669 Query
* query
= pending_transfer_queries_
.front().get();
670 if (!query
->Process(false)) {
673 if (query
->pending()) {
676 query
->RunCallbacks();
677 pending_transfer_queries_
.pop_front();
683 bool QueryManager::HavePendingTransferQueries() {
684 return !pending_transfer_queries_
.empty();
687 bool QueryManager::AddPendingQuery(Query
* query
,
688 base::subtle::Atomic32 submit_count
) {
690 DCHECK(!query
->IsDeleted());
691 if (!RemovePendingQuery(query
)) {
694 query
->MarkAsPending(submit_count
);
695 pending_queries_
.push_back(query
);
699 bool QueryManager::AddPendingTransferQuery(
701 base::subtle::Atomic32 submit_count
) {
703 DCHECK(!query
->IsDeleted());
704 if (!RemovePendingQuery(query
)) {
707 query
->MarkAsPending(submit_count
);
708 pending_transfer_queries_
.push_back(query
);
712 bool QueryManager::RemovePendingQuery(Query
* query
) {
714 if (query
->pending()) {
715 // TODO(gman): Speed this up if this is a common operation. This would only
716 // happen if you do being/end begin/end on the same query without waiting
717 // for the first one to finish.
718 for (QueryQueue::iterator it
= pending_queries_
.begin();
719 it
!= pending_queries_
.end(); ++it
) {
720 if (it
->get() == query
) {
721 pending_queries_
.erase(it
);
725 for (QueryQueue::iterator it
= pending_transfer_queries_
.begin();
726 it
!= pending_transfer_queries_
.end(); ++it
) {
727 if (it
->get() == query
) {
728 pending_transfer_queries_
.erase(it
);
732 if (!query
->MarkAsCompleted(0)) {
739 bool QueryManager::BeginQuery(Query
* query
) {
741 if (!RemovePendingQuery(query
)) {
744 return query
->Begin();
747 bool QueryManager::EndQuery(Query
* query
, base::subtle::Atomic32 submit_count
) {
749 if (!RemovePendingQuery(query
)) {
752 return query
->End(submit_count
);