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