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 "base/atomicops.h"
12 #include "base/numerics/safe_conversions.h"
13 #include "gpu/command_buffer/client/gles2_cmd_helper.h"
14 #include "gpu/command_buffer/client/gles2_implementation.h"
15 #include "gpu/command_buffer/client/mapped_memory.h"
16 #include "gpu/command_buffer/common/time.h"
21 QuerySyncManager::QuerySyncManager(MappedMemoryManager
* manager
)
22 : mapped_memory_(manager
) {
26 QuerySyncManager::~QuerySyncManager() {
27 while (!buckets_
.empty()) {
28 mapped_memory_
->Free(buckets_
.front()->syncs
);
29 delete buckets_
.front();
34 bool QuerySyncManager::Alloc(QuerySyncManager::QueryInfo
* info
) {
36 if (free_queries_
.empty()) {
38 unsigned int shm_offset
;
39 void* mem
= mapped_memory_
->Alloc(
40 kSyncsPerBucket
* sizeof(QuerySync
), &shm_id
, &shm_offset
);
44 QuerySync
* syncs
= static_cast<QuerySync
*>(mem
);
45 Bucket
* bucket
= new Bucket(syncs
);
46 buckets_
.push_back(bucket
);
47 for (size_t ii
= 0; ii
< kSyncsPerBucket
; ++ii
) {
48 free_queries_
.push_back(QueryInfo(bucket
, shm_id
, shm_offset
, syncs
));
50 shm_offset
+= sizeof(*syncs
);
53 *info
= free_queries_
.front();
54 ++(info
->bucket
->used_query_count
);
56 free_queries_
.pop_front();
60 void QuerySyncManager::Free(const QuerySyncManager::QueryInfo
& info
) {
61 DCHECK_GT(info
.bucket
->used_query_count
, 0u);
62 --(info
.bucket
->used_query_count
);
63 free_queries_
.push_back(info
);
66 void QuerySyncManager::Shrink() {
67 std::deque
<QueryInfo
> new_queue
;
68 while (!free_queries_
.empty()) {
69 if (free_queries_
.front().bucket
->used_query_count
)
70 new_queue
.push_back(free_queries_
.front());
71 free_queries_
.pop_front();
73 free_queries_
.swap(new_queue
);
75 std::deque
<Bucket
*> new_buckets
;
76 while (!buckets_
.empty()) {
77 Bucket
* bucket
= buckets_
.front();
78 if (bucket
->used_query_count
) {
79 new_buckets
.push_back(bucket
);
81 mapped_memory_
->Free(bucket
->syncs
);
86 buckets_
.swap(new_buckets
);
89 QueryTracker::Query::Query(GLuint id
, GLenum target
,
90 const QuerySyncManager::QueryInfo
& info
)
94 state_(kUninitialized
),
98 client_begin_time_us_(0),
103 void QueryTracker::Query::Begin(GLES2Implementation
* gl
) {
104 // init memory, inc count
108 case GL_GET_ERROR_QUERY_CHROMIUM
:
109 // To nothing on begin for error queries.
111 case GL_LATENCY_QUERY_CHROMIUM
:
112 client_begin_time_us_
= MicrosecondsSinceOriginOfTime();
113 // tell service about id, shared memory and count
114 gl
->helper()->BeginQueryEXT(target(), id(), shm_id(), shm_offset());
116 case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM
:
117 case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM
:
119 // tell service about id, shared memory and count
120 gl
->helper()->BeginQueryEXT(target(), id(), shm_id(), shm_offset());
125 void QueryTracker::Query::End(GLES2Implementation
* gl
) {
127 case GL_GET_ERROR_QUERY_CHROMIUM
: {
128 GLenum error
= gl
->GetClientSideGLError();
129 if (error
== GL_NO_ERROR
) {
130 // There was no error so start the query on the service.
131 // it will end immediately.
132 gl
->helper()->BeginQueryEXT(target(), id(), shm_id(), shm_offset());
134 // There's an error on the client, no need to bother the service. Just
135 // set the query as completed and return the error.
136 if (error
!= GL_NO_ERROR
) {
144 flush_count_
= gl
->helper()->flush_generation();
145 gl
->helper()->EndQueryEXT(target(), submit_count());
146 MarkAsPending(gl
->helper()->InsertToken());
149 bool QueryTracker::Query::CheckResultsAvailable(
150 CommandBufferHelper
* helper
) {
152 if (base::subtle::Acquire_Load(&info_
.sync
->process_count
) ==
154 helper
->IsContextLost()) {
156 case GL_COMMANDS_ISSUED_CHROMIUM
:
157 result_
= base::saturated_cast
<uint32
>(info_
.sync
->result
);
159 case GL_LATENCY_QUERY_CHROMIUM
:
160 // Disabled DCHECK because of http://crbug.com/419236.
161 //DCHECK(info_.sync->result >= client_begin_time_us_);
162 result_
= base::saturated_cast
<uint32
>(
163 info_
.sync
->result
- client_begin_time_us_
);
165 case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM
:
166 case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM
:
168 result_
= static_cast<uint32
>(info_
.sync
->result
);
173 if ((helper
->flush_generation() - flush_count_
- 1) >= 0x80000000) {
176 // Insert no-ops so that eventually the GPU process will see more work.
181 return state_
== kComplete
;
184 uint32
QueryTracker::Query::GetResult() const {
185 DCHECK(state_
== kComplete
|| state_
== kUninitialized
);
189 QueryTracker::QueryTracker(MappedMemoryManager
* manager
)
190 : query_sync_manager_(manager
) {
193 QueryTracker::~QueryTracker() {
194 while (!queries_
.empty()) {
195 delete queries_
.begin()->second
;
196 queries_
.erase(queries_
.begin());
198 while (!removed_queries_
.empty()) {
199 delete removed_queries_
.front();
200 removed_queries_
.pop_front();
204 QueryTracker::Query
* QueryTracker::CreateQuery(GLuint id
, GLenum target
) {
206 FreeCompletedQueries();
207 QuerySyncManager::QueryInfo info
;
208 if (!query_sync_manager_
.Alloc(&info
)) {
211 Query
* query
= new Query(id
, target
, info
);
212 std::pair
<QueryMap::iterator
, bool> result
=
213 queries_
.insert(std::make_pair(id
, query
));
214 DCHECK(result
.second
);
218 QueryTracker::Query
* QueryTracker::GetQuery(
220 QueryMap::iterator it
= queries_
.find(client_id
);
221 return it
!= queries_
.end() ? it
->second
: NULL
;
224 void QueryTracker::RemoveQuery(GLuint client_id
) {
225 QueryMap::iterator it
= queries_
.find(client_id
);
226 if (it
!= queries_
.end()) {
227 Query
* query
= it
->second
;
228 // When you delete a query you can't mark its memory as unused until it's
230 // Note: If you don't do this you won't mess up the service but you will
232 removed_queries_
.push_back(query
);
234 FreeCompletedQueries();
238 void QueryTracker::Shrink() {
239 FreeCompletedQueries();
240 query_sync_manager_
.Shrink();
243 void QueryTracker::FreeCompletedQueries() {
244 QueryList::iterator it
= removed_queries_
.begin();
245 while (it
!= removed_queries_
.end()) {
247 if (query
->Pending() &&
248 base::subtle::Acquire_Load(&query
->info_
.sync
->process_count
) !=
249 query
->submit_count()) {
254 query_sync_manager_
.Free(query
->info_
);
255 it
= removed_queries_
.erase(it
);