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/client/query_tracker.h"
8 #include <GLES2/gl2ext.h>
9 #include <GLES2/gl2extchromium.h>
11 #include "gpu/command_buffer/client/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"
20 QuerySyncManager::QuerySyncManager(MappedMemoryManager
* manager
)
21 : mapped_memory_(manager
) {
25 QuerySyncManager::~QuerySyncManager() {
26 while (!buckets_
.empty()) {
27 mapped_memory_
->Free(buckets_
.front()->syncs
);
28 delete buckets_
.front();
33 bool QuerySyncManager::Alloc(QuerySyncManager::QueryInfo
* info
) {
35 if (free_queries_
.empty()) {
37 unsigned int shm_offset
;
38 void* mem
= mapped_memory_
->Alloc(
39 kSyncsPerBucket
* sizeof(QuerySync
), &shm_id
, &shm_offset
);
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
));
49 shm_offset
+= sizeof(*syncs
);
52 *info
= free_queries_
.front();
53 ++(info
->bucket
->used_query_count
);
55 free_queries_
.pop_front();
59 void 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
);
65 void 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();
72 free_queries_
.swap(new_queue
);
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
);
80 mapped_memory_
->Free(bucket
->syncs
);
85 buckets_
.swap(new_buckets
);
88 QueryTracker::Query::Query(GLuint id
, GLenum target
,
89 const QuerySyncManager::QueryInfo
& info
)
93 state_(kUninitialized
),
97 client_begin_time_us_(0),
102 void QueryTracker::Query::Begin(GLES2Implementation
* gl
) {
103 // init memory, inc count
107 case GL_GET_ERROR_QUERY_CHROMIUM
:
108 // To nothing on begin for error queries.
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());
115 case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM
:
116 case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM
:
118 // tell service about id, shared memory and count
119 gl
->helper()->BeginQueryEXT(target(), id(), shm_id(), shm_offset());
124 void QueryTracker::Query::End(GLES2Implementation
* gl
) {
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 serivce.
130 // it will end immediately.
131 gl
->helper()->BeginQueryEXT(target(), id(), shm_id(), shm_offset());
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
) {
143 gl
->helper()->EndQueryEXT(target(), submit_count());
144 MarkAsPending(gl
->helper()->InsertToken());
147 bool QueryTracker::Query::CheckResultsAvailable(
148 CommandBufferHelper
* helper
) {
150 if (info_
.sync
->process_count
== submit_count_
||
151 helper
->IsContextLost()) {
152 // Need a MemoryBarrier here so that sync->result read after
153 // sync->process_count.
154 gpu::MemoryBarrier();
156 case GL_COMMANDS_ISSUED_CHROMIUM
:
157 result_
= std::min(info_
.sync
->result
,
158 static_cast<uint64
>(0xFFFFFFFFL
));
160 case GL_LATENCY_QUERY_CHROMIUM
:
161 GPU_DCHECK(info_
.sync
->result
>= client_begin_time_us_
);
162 result_
= std::min(info_
.sync
->result
- client_begin_time_us_
,
163 static_cast<uint64
>(0xFFFFFFFFL
));
165 case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM
:
166 case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM
:
168 result_
= info_
.sync
->result
;
174 // TODO(gman): We could reduce the number of flushes by having a
175 // flush count, recording that count at the time we insert the
176 // EndQuery command and then only flushing here if we've have not
177 // passed that count yet.
181 // Insert no-ops so that eventually the GPU process will see more work.
186 return state_
== kComplete
;
189 uint32
QueryTracker::Query::GetResult() const {
190 GPU_DCHECK(state_
== kComplete
|| state_
== kUninitialized
);
194 QueryTracker::QueryTracker(MappedMemoryManager
* manager
)
195 : query_sync_manager_(manager
) {
198 QueryTracker::~QueryTracker() {
199 while (!queries_
.empty()) {
200 delete queries_
.begin()->second
;
201 queries_
.erase(queries_
.begin());
203 while (!removed_queries_
.empty()) {
204 delete removed_queries_
.front();
205 removed_queries_
.pop_front();
209 QueryTracker::Query
* QueryTracker::CreateQuery(GLuint id
, GLenum target
) {
210 GPU_DCHECK_NE(0u, id
);
211 FreeCompletedQueries();
212 QuerySyncManager::QueryInfo info
;
213 if (!query_sync_manager_
.Alloc(&info
)) {
216 Query
* query
= new Query(id
, target
, info
);
217 std::pair
<QueryMap::iterator
, bool> result
=
218 queries_
.insert(std::make_pair(id
, query
));
219 GPU_DCHECK(result
.second
);
223 QueryTracker::Query
* QueryTracker::GetQuery(
225 QueryMap::iterator it
= queries_
.find(client_id
);
226 return it
!= queries_
.end() ? it
->second
: NULL
;
229 void QueryTracker::RemoveQuery(GLuint client_id
) {
230 QueryMap::iterator it
= queries_
.find(client_id
);
231 if (it
!= queries_
.end()) {
232 Query
* query
= it
->second
;
233 // When you delete a query you can't mark its memory as unused until it's
235 // Note: If you don't do this you won't mess up the service but you will
237 removed_queries_
.push_back(query
);
239 FreeCompletedQueries();
243 void QueryTracker::Shrink() {
244 FreeCompletedQueries();
245 query_sync_manager_
.Shrink();
248 void QueryTracker::FreeCompletedQueries() {
249 QueryList::iterator it
= removed_queries_
.begin();
250 while (it
!= removed_queries_
.end()) {
252 if (query
->Pending() &&
253 query
->info_
.sync
->process_count
!= query
->submit_count()) {
258 query_sync_manager_
.Free(query
->info_
);
259 it
= removed_queries_
.erase(it
);