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/client/query_tracker.h"
6
7#include <GLES2/gl2.h>
8#include <GLES2/gl2ext.h>
9#include <GLES2/gl2extchromium.h>
10
11#include "base/atomicops.h"
12#include "gpu/command_buffer/client/gles2_cmd_helper.h"
13#include "gpu/command_buffer/client/gles2_implementation.h"
14#include "gpu/command_buffer/client/mapped_memory.h"
15#include "gpu/command_buffer/common/time.h"
16
17namespace gpu {
18namespace gles2 {
19
20QuerySyncManager::QuerySyncManager(MappedMemoryManager* manager)
21    : mapped_memory_(manager) {
22  DCHECK(manager);
23}
24
25QuerySyncManager::~QuerySyncManager() {
26  while (!buckets_.empty()) {
27    mapped_memory_->Free(buckets_.front()->syncs);
28    delete buckets_.front();
29    buckets_.pop_front();
30  }
31}
32
33bool QuerySyncManager::Alloc(QuerySyncManager::QueryInfo* info) {
34  DCHECK(info);
35  if (free_queries_.empty()) {
36    int32 shm_id;
37    unsigned int shm_offset;
38    void* mem = mapped_memory_->Alloc(
39        kSyncsPerBucket * sizeof(QuerySync), &shm_id, &shm_offset);
40    if (!mem) {
41      return false;
42    }
43    QuerySync* syncs = static_cast<QuerySync*>(mem);
44    Bucket* bucket = new Bucket(syncs);
45    buckets_.push_back(bucket);
46    for (size_t ii = 0; ii < kSyncsPerBucket; ++ii) {
47      free_queries_.push_back(QueryInfo(bucket, shm_id, shm_offset, syncs));
48      ++syncs;
49      shm_offset += sizeof(*syncs);
50    }
51  }
52  *info = free_queries_.front();
53  ++(info->bucket->used_query_count);
54  info->sync->Reset();
55  free_queries_.pop_front();
56  return true;
57}
58
59void QuerySyncManager::Free(const QuerySyncManager::QueryInfo& info) {
60  DCHECK_GT(info.bucket->used_query_count, 0u);
61  --(info.bucket->used_query_count);
62  free_queries_.push_back(info);
63}
64
65void QuerySyncManager::Shrink() {
66  std::deque<QueryInfo> new_queue;
67  while (!free_queries_.empty()) {
68    if (free_queries_.front().bucket->used_query_count)
69      new_queue.push_back(free_queries_.front());
70    free_queries_.pop_front();
71  }
72  free_queries_.swap(new_queue);
73
74  std::deque<Bucket*> new_buckets;
75  while (!buckets_.empty()) {
76    Bucket* bucket = buckets_.front();
77    if (bucket->used_query_count) {
78      new_buckets.push_back(bucket);
79    } else {
80      mapped_memory_->Free(bucket->syncs);
81      delete bucket;
82    }
83    buckets_.pop_front();
84  }
85  buckets_.swap(new_buckets);
86}
87
88QueryTracker::Query::Query(GLuint id, GLenum target,
89                           const QuerySyncManager::QueryInfo& info)
90    : id_(id),
91      target_(target),
92      info_(info),
93      state_(kUninitialized),
94      submit_count_(0),
95      token_(0),
96      flush_count_(0),
97      client_begin_time_us_(0),
98      result_(0) {
99    }
100
101
102void QueryTracker::Query::Begin(GLES2Implementation* gl) {
103  // init memory, inc count
104  MarkAsActive();
105
106  switch (target()) {
107    case GL_GET_ERROR_QUERY_CHROMIUM:
108      // To nothing on begin for error queries.
109      break;
110    case GL_LATENCY_QUERY_CHROMIUM:
111      client_begin_time_us_ = MicrosecondsSinceOriginOfTime();
112      // tell service about id, shared memory and count
113      gl->helper()->BeginQueryEXT(target(), id(), shm_id(), shm_offset());
114      break;
115    case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM:
116    case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM:
117    default:
118      // tell service about id, shared memory and count
119      gl->helper()->BeginQueryEXT(target(), id(), shm_id(), shm_offset());
120      break;
121  }
122}
123
124void QueryTracker::Query::End(GLES2Implementation* gl) {
125  switch (target()) {
126    case GL_GET_ERROR_QUERY_CHROMIUM: {
127      GLenum error = gl->GetClientSideGLError();
128      if (error == GL_NO_ERROR) {
129        // There was no error so start the query on the service.
130        // it will end immediately.
131        gl->helper()->BeginQueryEXT(target(), id(), shm_id(), shm_offset());
132      } else {
133        // There's an error on the client, no need to bother the service. Just
134        // set the query as completed and return the error.
135        if (error != GL_NO_ERROR) {
136          state_ = kComplete;
137          result_ = error;
138          return;
139        }
140      }
141    }
142  }
143  flush_count_ = gl->helper()->flush_generation();
144  gl->helper()->EndQueryEXT(target(), submit_count());
145  MarkAsPending(gl->helper()->InsertToken());
146}
147
148bool QueryTracker::Query::CheckResultsAvailable(
149    CommandBufferHelper* helper) {
150  if (Pending()) {
151    if (base::subtle::Acquire_Load(&info_.sync->process_count) ==
152            submit_count_ ||
153        helper->IsContextLost()) {
154      switch (target()) {
155        case GL_COMMANDS_ISSUED_CHROMIUM:
156          result_ = std::min(info_.sync->result,
157                             static_cast<uint64>(0xFFFFFFFFL));
158          break;
159        case GL_LATENCY_QUERY_CHROMIUM:
160          DCHECK(info_.sync->result >= client_begin_time_us_);
161          result_ = std::min(info_.sync->result - client_begin_time_us_,
162                             static_cast<uint64>(0xFFFFFFFFL));
163          break;
164        case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM:
165        case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM:
166        default:
167          result_ = info_.sync->result;
168          break;
169      }
170      state_ = kComplete;
171    } else {
172      if ((helper->flush_generation() - flush_count_ - 1) >= 0x80000000) {
173        helper->Flush();
174      } else {
175        // Insert no-ops so that eventually the GPU process will see more work.
176        helper->Noop(1);
177      }
178    }
179  }
180  return state_ == kComplete;
181}
182
183uint32 QueryTracker::Query::GetResult() const {
184  DCHECK(state_ == kComplete || state_ == kUninitialized);
185  return result_;
186}
187
188QueryTracker::QueryTracker(MappedMemoryManager* manager)
189    : query_sync_manager_(manager) {
190}
191
192QueryTracker::~QueryTracker() {
193  while (!queries_.empty()) {
194    delete queries_.begin()->second;
195    queries_.erase(queries_.begin());
196  }
197  while (!removed_queries_.empty()) {
198    delete removed_queries_.front();
199    removed_queries_.pop_front();
200  }
201}
202
203QueryTracker::Query* QueryTracker::CreateQuery(GLuint id, GLenum target) {
204  DCHECK_NE(0u, id);
205  FreeCompletedQueries();
206  QuerySyncManager::QueryInfo info;
207  if (!query_sync_manager_.Alloc(&info)) {
208    return NULL;
209  }
210  Query* query = new Query(id, target, info);
211  std::pair<QueryMap::iterator, bool> result =
212      queries_.insert(std::make_pair(id, query));
213  DCHECK(result.second);
214  return query;
215}
216
217QueryTracker::Query* QueryTracker::GetQuery(
218    GLuint client_id) {
219  QueryMap::iterator it = queries_.find(client_id);
220  return it != queries_.end() ? it->second : NULL;
221}
222
223void QueryTracker::RemoveQuery(GLuint client_id) {
224  QueryMap::iterator it = queries_.find(client_id);
225  if (it != queries_.end()) {
226    Query* query = it->second;
227    // When you delete a query you can't mark its memory as unused until it's
228    // completed.
229    // Note: If you don't do this you won't mess up the service but you will
230    // mess up yourself.
231    removed_queries_.push_back(query);
232    queries_.erase(it);
233    FreeCompletedQueries();
234  }
235}
236
237void QueryTracker::Shrink() {
238  FreeCompletedQueries();
239  query_sync_manager_.Shrink();
240}
241
242void QueryTracker::FreeCompletedQueries() {
243  QueryList::iterator it = removed_queries_.begin();
244  while (it != removed_queries_.end()) {
245    Query* query = *it;
246    if (query->Pending() &&
247        base::subtle::Acquire_Load(&query->info_.sync->process_count) !=
248            query->submit_count()) {
249      ++it;
250      continue;
251    }
252
253    query_sync_manager_.Free(query->info_);
254    it = removed_queries_.erase(it);
255    delete query;
256  }
257}
258
259}  // namespace gles2
260}  // namespace gpu
261