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 virtual 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 virtual ~AsyncPixelTransferCompletionObserverImpl() {}
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 virtual bool Begin() OVERRIDE
;
66 virtual bool End(base::subtle::Atomic32 submit_count
) OVERRIDE
;
67 virtual bool Process() OVERRIDE
;
68 virtual void Destroy(bool have_context
) OVERRIDE
;
71 virtual ~AsyncPixelTransfersCompletedQuery();
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() {
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 virtual bool Begin() OVERRIDE
;
143 virtual bool End(base::subtle::Atomic32 submit_count
) OVERRIDE
;
144 virtual bool Process() OVERRIDE
;
145 virtual void Destroy(bool have_context
) OVERRIDE
;
148 virtual ~AllSamplesPassedQuery();
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() {
173 GLuint available
= 0;
174 glGetQueryObjectuivARB(
175 service_id_
, GL_QUERY_RESULT_AVAILABLE_EXT
, &available
);
180 glGetQueryObjectuivARB(
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 glDeleteQueriesARB(1, &service_id_
);
193 AllSamplesPassedQuery::~AllSamplesPassedQuery() {
196 class CommandsIssuedQuery
: public QueryManager::Query
{
199 QueryManager
* manager
, GLenum target
, int32 shm_id
, uint32 shm_offset
);
201 virtual bool Begin() OVERRIDE
;
202 virtual bool End(base::subtle::Atomic32 submit_count
) OVERRIDE
;
203 virtual bool Process() OVERRIDE
;
204 virtual void Destroy(bool have_context
) OVERRIDE
;
207 virtual ~CommandsIssuedQuery();
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::HighResNow();
223 bool CommandsIssuedQuery::End(base::subtle::Atomic32 submit_count
) {
224 base::TimeDelta elapsed
= base::TimeTicks::HighResNow() - begin_time_
;
225 MarkAsPending(submit_count
);
226 return MarkAsCompleted(elapsed
.InMicroseconds());
229 bool CommandsIssuedQuery::Process() {
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 virtual bool Begin() OVERRIDE
;
249 virtual bool End(base::subtle::Atomic32 submit_count
) OVERRIDE
;
250 virtual bool Process() OVERRIDE
;
251 virtual void Destroy(bool have_context
) OVERRIDE
;
254 virtual ~CommandLatencyQuery();
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::HighResNow() - base::TimeTicks();
268 MarkAsPending(submit_count
);
269 return MarkAsCompleted(now
.InMicroseconds());
272 bool CommandLatencyQuery::Process() {
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 virtual bool Begin() OVERRIDE
;
295 virtual bool End(base::subtle::Atomic32 submit_count
) OVERRIDE
;
296 virtual bool Process() OVERRIDE
;
297 virtual void Destroy(bool have_context
) OVERRIDE
;
301 virtual ~AsyncReadPixelsCompletedQuery();
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
,
330 void AsyncReadPixelsCompletedQuery::Complete() {
332 complete_result_
= MarkAsCompleted(1);
335 bool AsyncReadPixelsCompletedQuery::Process() {
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 virtual bool Begin() OVERRIDE
;
355 virtual bool End(base::subtle::Atomic32 submit_count
) OVERRIDE
;
356 virtual bool Process() OVERRIDE
;
357 virtual void Destroy(bool have_context
) OVERRIDE
;
360 virtual ~GetErrorQuery();
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() {
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 virtual bool Begin() OVERRIDE
;
402 virtual bool End(base::subtle::Atomic32 submit_count
) OVERRIDE
;
403 virtual bool Process() OVERRIDE
;
404 virtual void Destroy(bool have_context
) OVERRIDE
;
407 virtual ~CommandsCompletedQuery();
410 scoped_ptr
<gfx::GLFence
> fence_
;
413 CommandsCompletedQuery::CommandsCompletedQuery(QueryManager
* manager
,
417 : Query(manager
, target
, shm_id
, shm_offset
) {}
419 bool CommandsCompletedQuery::Begin() { return true; }
421 bool CommandsCompletedQuery::End(base::subtle::Atomic32 submit_count
) {
422 fence_
.reset(gfx::GLFence::Create());
424 return AddToPendingQueue(submit_count
);
427 bool CommandsCompletedQuery::Process() {
428 if (fence_
&& !fence_
->HasCompleted())
430 return MarkAsCompleted(0);
433 void CommandsCompletedQuery::Destroy(bool have_context
) {
434 if (have_context
&& !IsDeleted()) {
440 CommandsCompletedQuery::~CommandsCompletedQuery() {}
442 QueryManager::QueryManager(
443 GLES2Decoder
* decoder
,
444 FeatureInfo
* feature_info
)
446 use_arb_occlusion_query2_for_occlusion_query_boolean_(
447 feature_info
->feature_flags(
448 ).use_arb_occlusion_query2_for_occlusion_query_boolean
),
449 use_arb_occlusion_query_for_occlusion_query_boolean_(
450 feature_info
->feature_flags(
451 ).use_arb_occlusion_query_for_occlusion_query_boolean
),
453 DCHECK(!(use_arb_occlusion_query_for_occlusion_query_boolean_
&&
454 use_arb_occlusion_query2_for_occlusion_query_boolean_
));
457 QueryManager::~QueryManager() {
458 DCHECK(queries_
.empty());
460 // If this triggers, that means something is keeping a reference to
461 // a Query belonging to this.
462 CHECK_EQ(query_count_
, 0u);
465 void QueryManager::Destroy(bool have_context
) {
466 pending_queries_
.clear();
467 pending_transfer_queries_
.clear();
468 while (!queries_
.empty()) {
469 Query
* query
= queries_
.begin()->second
.get();
470 query
->Destroy(have_context
);
471 queries_
.erase(queries_
.begin());
475 QueryManager::Query
* QueryManager::CreateQuery(
476 GLenum target
, GLuint client_id
, int32 shm_id
, uint32 shm_offset
) {
477 scoped_refptr
<Query
> query
;
479 case GL_COMMANDS_ISSUED_CHROMIUM
:
480 query
= new CommandsIssuedQuery(this, target
, shm_id
, shm_offset
);
482 case GL_LATENCY_QUERY_CHROMIUM
:
483 query
= new CommandLatencyQuery(this, target
, shm_id
, shm_offset
);
485 case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM
:
486 // Currently async pixel transfer delegates only support uploads.
487 query
= new AsyncPixelTransfersCompletedQuery(
488 this, target
, shm_id
, shm_offset
);
490 case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM
:
491 query
= new AsyncReadPixelsCompletedQuery(
492 this, target
, shm_id
, shm_offset
);
494 case GL_GET_ERROR_QUERY_CHROMIUM
:
495 query
= new GetErrorQuery(this, target
, shm_id
, shm_offset
);
497 case GL_COMMANDS_COMPLETED_CHROMIUM
:
498 query
= new CommandsCompletedQuery(this, target
, shm_id
, shm_offset
);
501 GLuint service_id
= 0;
502 glGenQueriesARB(1, &service_id
);
503 DCHECK_NE(0u, service_id
);
504 query
= new AllSamplesPassedQuery(
505 this, target
, shm_id
, shm_offset
, service_id
);
509 std::pair
<QueryMap::iterator
, bool> result
=
510 queries_
.insert(std::make_pair(client_id
, query
));
511 DCHECK(result
.second
);
515 void QueryManager::GenQueries(GLsizei n
, const GLuint
* queries
) {
517 for (GLsizei i
= 0; i
< n
; ++i
) {
518 generated_query_ids_
.insert(queries
[i
]);
522 bool QueryManager::IsValidQuery(GLuint id
) {
523 GeneratedQueryIds::iterator it
= generated_query_ids_
.find(id
);
524 return it
!= generated_query_ids_
.end();
527 QueryManager::Query
* QueryManager::GetQuery(
529 QueryMap::iterator it
= queries_
.find(client_id
);
530 return it
!= queries_
.end() ? it
->second
.get() : NULL
;
533 void QueryManager::RemoveQuery(GLuint client_id
) {
534 QueryMap::iterator it
= queries_
.find(client_id
);
535 if (it
!= queries_
.end()) {
536 Query
* query
= it
->second
.get();
537 RemovePendingQuery(query
);
538 query
->MarkAsDeleted();
541 generated_query_ids_
.erase(client_id
);
544 void QueryManager::StartTracking(QueryManager::Query
* /* query */) {
548 void QueryManager::StopTracking(QueryManager::Query
* /* query */) {
552 GLenum
QueryManager::AdjustTargetForEmulation(GLenum target
) {
554 case GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT
:
555 case GL_ANY_SAMPLES_PASSED_EXT
:
556 if (use_arb_occlusion_query2_for_occlusion_query_boolean_
) {
557 // ARB_occlusion_query2 does not have a
558 // GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT
560 target
= GL_ANY_SAMPLES_PASSED_EXT
;
561 } else if (use_arb_occlusion_query_for_occlusion_query_boolean_
) {
562 // ARB_occlusion_query does not have a
563 // GL_ANY_SAMPLES_PASSED_EXT
565 target
= GL_SAMPLES_PASSED_ARB
;
574 void QueryManager::BeginQueryHelper(GLenum target
, GLuint id
) {
575 target
= AdjustTargetForEmulation(target
);
576 glBeginQueryARB(target
, id
);
579 void QueryManager::EndQueryHelper(GLenum target
) {
580 target
= AdjustTargetForEmulation(target
);
581 glEndQueryARB(target
);
584 QueryManager::Query::Query(
585 QueryManager
* manager
, GLenum target
, int32 shm_id
, uint32 shm_offset
)
589 shm_offset_(shm_offset
),
594 manager_
->StartTracking(this);
597 void QueryManager::Query::RunCallbacks() {
598 for (size_t i
= 0; i
< callbacks_
.size(); i
++) {
604 void QueryManager::Query::AddCallback(base::Closure callback
) {
606 callbacks_
.push_back(callback
);
612 QueryManager::Query::~Query() {
613 // The query is getting deleted, either by the client or
614 // because the context was lost. Call any outstanding
615 // callbacks to avoid leaks.
618 manager_
->StopTracking(this);
623 bool QueryManager::Query::MarkAsCompleted(uint64 result
) {
625 QuerySync
* sync
= manager_
->decoder_
->GetSharedMemoryAs
<QuerySync
*>(
626 shm_id_
, shm_offset_
, sizeof(*sync
));
632 sync
->result
= result
;
633 base::subtle::Release_Store(&sync
->process_count
, submit_count_
);
638 bool QueryManager::ProcessPendingQueries() {
639 while (!pending_queries_
.empty()) {
640 Query
* query
= pending_queries_
.front().get();
641 if (!query
->Process()) {
644 if (query
->pending()) {
647 query
->RunCallbacks();
648 pending_queries_
.pop_front();
654 bool QueryManager::HavePendingQueries() {
655 return !pending_queries_
.empty();
658 bool QueryManager::ProcessPendingTransferQueries() {
659 while (!pending_transfer_queries_
.empty()) {
660 Query
* query
= pending_transfer_queries_
.front().get();
661 if (!query
->Process()) {
664 if (query
->pending()) {
667 query
->RunCallbacks();
668 pending_transfer_queries_
.pop_front();
674 bool QueryManager::HavePendingTransferQueries() {
675 return !pending_transfer_queries_
.empty();
678 bool QueryManager::AddPendingQuery(Query
* query
,
679 base::subtle::Atomic32 submit_count
) {
681 DCHECK(!query
->IsDeleted());
682 if (!RemovePendingQuery(query
)) {
685 query
->MarkAsPending(submit_count
);
686 pending_queries_
.push_back(query
);
690 bool QueryManager::AddPendingTransferQuery(
692 base::subtle::Atomic32 submit_count
) {
694 DCHECK(!query
->IsDeleted());
695 if (!RemovePendingQuery(query
)) {
698 query
->MarkAsPending(submit_count
);
699 pending_transfer_queries_
.push_back(query
);
703 bool QueryManager::RemovePendingQuery(Query
* query
) {
705 if (query
->pending()) {
706 // TODO(gman): Speed this up if this is a common operation. This would only
707 // happen if you do being/end begin/end on the same query without waiting
708 // for the first one to finish.
709 for (QueryQueue::iterator it
= pending_queries_
.begin();
710 it
!= pending_queries_
.end(); ++it
) {
711 if (it
->get() == query
) {
712 pending_queries_
.erase(it
);
716 for (QueryQueue::iterator it
= pending_transfer_queries_
.begin();
717 it
!= pending_transfer_queries_
.end(); ++it
) {
718 if (it
->get() == query
) {
719 pending_transfer_queries_
.erase(it
);
723 if (!query
->MarkAsCompleted(0)) {
730 bool QueryManager::BeginQuery(Query
* query
) {
732 if (!RemovePendingQuery(query
)) {
735 return query
->Begin();
738 bool QueryManager::EndQuery(Query
* query
, base::subtle::Atomic32 submit_count
) {
740 if (!RemovePendingQuery(query
)) {
743 return query
->End(submit_count
);