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
19namespace gpu {
20namespace gles2 {
21
22namespace {
23
24class 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
63class 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
81AsyncPixelTransfersCompletedQuery::AsyncPixelTransfersCompletedQuery(
82    QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset)
83    : Query(manager, target, shm_id, shm_offset) {
84}
85
86bool AsyncPixelTransfersCompletedQuery::Begin() {
87  return true;
88}
89
90bool 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
116bool 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
132void AsyncPixelTransfersCompletedQuery::Destroy(bool /* have_context */) {
133  if (!IsDeleted()) {
134    MarkAsDeleted();
135  }
136}
137
138AsyncPixelTransfersCompletedQuery::~AsyncPixelTransfersCompletedQuery() {
139  if (observer_)
140    observer_->Cancel();
141}
142
143}  // namespace
144
145class 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
163AllSamplesPassedQuery::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
170bool AllSamplesPassedQuery::Begin() {
171  BeginQueryHelper(target(), service_id_);
172  return true;
173}
174
175bool AllSamplesPassedQuery::End(uint32 submit_count) {
176  EndQueryHelper(target());
177  return AddToPendingQueue(submit_count);
178}
179
180bool 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
194void AllSamplesPassedQuery::Destroy(bool have_context) {
195  if (have_context && !IsDeleted()) {
196    glDeleteQueriesARB(1, &service_id_);
197    MarkAsDeleted();
198  }
199}
200
201AllSamplesPassedQuery::~AllSamplesPassedQuery() {
202}
203
204class 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
221CommandsIssuedQuery::CommandsIssuedQuery(
222      QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset)
223    : Query(manager, target, shm_id, shm_offset) {
224}
225
226bool CommandsIssuedQuery::Begin() {
227  begin_time_ = base::TimeTicks::HighResNow();
228  return true;
229}
230
231bool 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
237bool CommandsIssuedQuery::Process() {
238  NOTREACHED();
239  return true;
240}
241
242void CommandsIssuedQuery::Destroy(bool /* have_context */) {
243  if (!IsDeleted()) {
244    MarkAsDeleted();
245  }
246}
247
248CommandsIssuedQuery::~CommandsIssuedQuery() {
249}
250
251class 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
265CommandLatencyQuery::CommandLatencyQuery(
266      QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset)
267    : Query(manager, target, shm_id, shm_offset) {
268}
269
270bool CommandLatencyQuery::Begin() {
271    return true;
272}
273
274bool 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
280bool CommandLatencyQuery::Process() {
281  NOTREACHED();
282  return true;
283}
284
285void CommandLatencyQuery::Destroy(bool /* have_context */) {
286  if (!IsDeleted()) {
287    MarkAsDeleted();
288  }
289}
290
291CommandLatencyQuery::~CommandLatencyQuery() {
292}
293
294
295class 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
312AsyncReadPixelsCompletedQuery::AsyncReadPixelsCompletedQuery(
313    QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset)
314    : Query(manager, target, shm_id, shm_offset) {
315}
316
317bool AsyncReadPixelsCompletedQuery::Begin() {
318  return true;
319}
320
321bool 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
332void AsyncReadPixelsCompletedQuery::Complete() {
333  MarkAsCompleted(1);
334}
335
336bool AsyncReadPixelsCompletedQuery::Process() {
337  return true;
338}
339
340void AsyncReadPixelsCompletedQuery::Destroy(bool /* have_context */) {
341  if (!IsDeleted()) {
342    MarkAsDeleted();
343  }
344}
345
346AsyncReadPixelsCompletedQuery::~AsyncReadPixelsCompletedQuery() {
347}
348
349
350class 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
366GetErrorQuery::GetErrorQuery(
367      QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset)
368    : Query(manager, target, shm_id, shm_offset) {
369}
370
371bool GetErrorQuery::Begin() {
372  return true;
373}
374
375bool GetErrorQuery::End(uint32 submit_count) {
376  MarkAsPending(submit_count);
377  return MarkAsCompleted(manager()->decoder()->GetErrorState()->GetGLError());
378}
379
380bool GetErrorQuery::Process() {
381  NOTREACHED();
382  return true;
383}
384
385void GetErrorQuery::Destroy(bool /* have_context */) {
386  if (!IsDeleted()) {
387    MarkAsDeleted();
388  }
389}
390
391GetErrorQuery::~GetErrorQuery() {
392}
393
394QueryManager::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
409QueryManager::~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
417void 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
427QueryManager::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
464QueryManager::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
470void 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
480void QueryManager::StartTracking(QueryManager::Query* /* query */) {
481  ++query_count_;
482}
483
484void QueryManager::StopTracking(QueryManager::Query* /* query */) {
485  --query_count_;
486}
487
488GLenum 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
510void QueryManager::BeginQueryHelper(GLenum target, GLuint id) {
511  target = AdjustTargetForEmulation(target);
512  glBeginQueryARB(target, id);
513}
514
515void QueryManager::EndQueryHelper(GLenum target) {
516  target = AdjustTargetForEmulation(target);
517  glEndQueryARB(target);
518}
519
520QueryManager::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
533void QueryManager::Query::RunCallbacks() {
534  for (size_t i = 0; i < callbacks_.size(); i++) {
535    callbacks_[i].Run();
536  }
537  callbacks_.clear();
538}
539
540void QueryManager::Query::AddCallback(base::Closure callback) {
541  if (pending_) {
542    callbacks_.push_back(callback);
543  } else {
544    callback.Run();
545  }
546}
547
548QueryManager::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
559bool 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
577bool 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
593bool QueryManager::HavePendingQueries() {
594  return !pending_queries_.empty();
595}
596
597bool 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
613bool QueryManager::HavePendingTransferQueries() {
614  return !pending_transfer_queries_.empty();
615}
616
617bool 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
628bool 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
639bool 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
666bool QueryManager::BeginQuery(Query* query) {
667  DCHECK(query);
668  if (!RemovePendingQuery(query)) {
669    return false;
670  }
671  return query->Begin();
672}
673
674bool 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