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/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"
19 QuerySyncManager::QuerySyncManager(MappedMemoryManager
* manager
)
20 : mapped_memory_(manager
) {
24 QuerySyncManager::~QuerySyncManager() {
25 while (!buckets_
.empty()) {
26 mapped_memory_
->Free(buckets_
.front()->syncs
);
27 delete buckets_
.front();
32 bool QuerySyncManager::Alloc(QuerySyncManager::QueryInfo
* info
) {
34 if (free_queries_
.empty()) {
36 unsigned int shm_offset
;
37 void* mem
= mapped_memory_
->Alloc(
38 kSyncsPerBucket
* sizeof(QuerySync
), &shm_id
, &shm_offset
);
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
));
48 shm_offset
+= sizeof(*syncs
);
51 *info
= free_queries_
.front();
52 ++(info
->bucket
->used_query_count
);
54 free_queries_
.pop_front();
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
);
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();
71 free_queries_
.swap(new_queue
);
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
);
79 mapped_memory_
->Free(bucket
->syncs
);
84 buckets_
.swap(new_buckets
);
87 QueryTracker::Query::Query(GLuint id
, GLenum target
,
88 const QuerySyncManager::QueryInfo
& info
)
92 state_(kUninitialized
),
96 client_begin_time_us_(0),
101 void QueryTracker::Query::Begin(GLES2Implementation
* gl
) {
102 // init memory, inc count
106 case GL_GET_ERROR_QUERY_CHROMIUM
:
107 // To nothing on begin for error queries.
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());
114 case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM
:
115 case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM
:
117 // tell service about id, shared memory and count
118 gl
->helper()->BeginQueryEXT(target(), id(), shm_id(), shm_offset());
123 void QueryTracker::Query::End(GLES2Implementation
* gl
) {
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());
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
) {
142 gl
->helper()->EndQueryEXT(target(), submit_count());
143 MarkAsPending(gl
->helper()->InsertToken());
146 bool QueryTracker::Query::CheckResultsAvailable(
147 CommandBufferHelper
* helper
) {
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();
155 case GL_COMMANDS_ISSUED_CHROMIUM
:
156 result_
= std::min(info_
.sync
->result
,
157 static_cast<uint64
>(0xFFFFFFFFL
));
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
));
164 case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM
:
165 case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM
:
167 result_
= info_
.sync
->result
;
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.
180 // Insert no-ops so that eventually the GPU process will see more work.
185 return state_
== kComplete
;
188 uint32
QueryTracker::Query::GetResult() const {
189 DCHECK(state_
== kComplete
|| state_
== kUninitialized
);
193 QueryTracker::QueryTracker(MappedMemoryManager
* manager
)
194 : query_sync_manager_(manager
) {
197 QueryTracker::~QueryTracker() {
198 while (!queries_
.empty()) {
199 delete queries_
.begin()->second
;
200 queries_
.erase(queries_
.begin());
202 while (!removed_queries_
.empty()) {
203 delete removed_queries_
.front();
204 removed_queries_
.pop_front();
208 QueryTracker::Query
* QueryTracker::CreateQuery(GLuint id
, GLenum target
) {
210 FreeCompletedQueries();
211 QuerySyncManager::QueryInfo info
;
212 if (!query_sync_manager_
.Alloc(&info
)) {
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
);
222 QueryTracker::Query
* QueryTracker::GetQuery(
224 QueryMap::iterator it
= queries_
.find(client_id
);
225 return it
!= queries_
.end() ? it
->second
: NULL
;
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
234 // Note: If you don't do this you won't mess up the service but you will
236 removed_queries_
.push_back(query
);
238 FreeCompletedQueries();
242 void QueryTracker::Shrink() {
243 FreeCompletedQueries();
244 query_sync_manager_
.Shrink();
247 void QueryTracker::FreeCompletedQueries() {
248 QueryList::iterator it
= removed_queries_
.begin();
249 while (it
!= removed_queries_
.end()) {
251 if (query
->Pending() &&
252 query
->info_
.sync
->process_count
!= query
->submit_count()) {
257 query_sync_manager_
.Free(query
->info_
);
258 it
= removed_queries_
.erase(it
);