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>
13 #include "base/atomicops.h"
14 #include "base/numerics/safe_conversions.h"
15 #include "gpu/command_buffer/client/gles2_cmd_helper.h"
16 #include "gpu/command_buffer/client/gles2_implementation.h"
17 #include "gpu/command_buffer/client/mapped_memory.h"
18 #include "gpu/command_buffer/common/time.h"
23 QuerySyncManager::Bucket::Bucket(QuerySync
* sync_mem
,
25 unsigned int shm_offset
)
28 base_shm_offset(shm_offset
) {
31 QuerySyncManager::Bucket::~Bucket() = default;
33 QuerySyncManager::QuerySyncManager(MappedMemoryManager
* manager
)
34 : mapped_memory_(manager
) {
38 QuerySyncManager::~QuerySyncManager() {
39 while (!buckets_
.empty()) {
40 mapped_memory_
->Free(buckets_
.front()->syncs
);
41 delete buckets_
.front();
46 bool QuerySyncManager::Alloc(QuerySyncManager::QueryInfo
* info
) {
48 Bucket
* bucket
= nullptr;
49 for (Bucket
* bucket_candidate
: buckets_
) {
50 // In C++11 STL this could be replaced with
51 // if (!bucket_candidate->in_use_queries.all()) { ... }
52 if (bucket_candidate
->in_use_queries
.count() != kSyncsPerBucket
) {
53 bucket
= bucket_candidate
;
59 unsigned int shm_offset
;
60 void* mem
= mapped_memory_
->Alloc(
61 kSyncsPerBucket
* sizeof(QuerySync
), &shm_id
, &shm_offset
);
65 QuerySync
* syncs
= static_cast<QuerySync
*>(mem
);
66 bucket
= new Bucket(syncs
, shm_id
, shm_offset
);
67 buckets_
.push_back(bucket
);
70 unsigned short index_in_bucket
= 0;
71 for (size_t i
= 0; i
< kSyncsPerBucket
; i
++) {
72 if (!bucket
->in_use_queries
[i
]) {
79 bucket
->base_shm_offset
+ index_in_bucket
* sizeof(QuerySync
);
80 QuerySync
* sync
= bucket
->syncs
+ index_in_bucket
;
81 *info
= QueryInfo(bucket
, bucket
->shm_id
, shm_offset
, sync
);
83 bucket
->in_use_queries
[index_in_bucket
] = true;
87 void QuerySyncManager::Free(const QuerySyncManager::QueryInfo
& info
) {
88 DCHECK_NE(info
.bucket
->in_use_queries
.count(), 0u);
89 unsigned short index_in_bucket
= info
.sync
- info
.bucket
->syncs
;
90 DCHECK(info
.bucket
->in_use_queries
[index_in_bucket
]);
91 info
.bucket
->in_use_queries
[index_in_bucket
] = false;
94 void QuerySyncManager::Shrink() {
95 std::deque
<Bucket
*> new_buckets
;
96 while (!buckets_
.empty()) {
97 Bucket
* bucket
= buckets_
.front();
98 if (bucket
->in_use_queries
.any()) {
99 new_buckets
.push_back(bucket
);
101 mapped_memory_
->Free(bucket
->syncs
);
104 buckets_
.pop_front();
106 buckets_
.swap(new_buckets
);
109 QueryTracker::Query::Query(GLuint id
, GLenum target
,
110 const QuerySyncManager::QueryInfo
& info
)
114 state_(kUninitialized
),
118 client_begin_time_us_(0),
123 void QueryTracker::Query::Begin(GLES2Implementation
* gl
) {
124 // init memory, inc count
128 case GL_GET_ERROR_QUERY_CHROMIUM
:
129 // To nothing on begin for error queries.
131 case GL_LATENCY_QUERY_CHROMIUM
:
132 client_begin_time_us_
= MicrosecondsSinceOriginOfTime();
133 // tell service about id, shared memory and count
134 gl
->helper()->BeginQueryEXT(target(), id(), shm_id(), shm_offset());
136 case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM
:
137 case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM
:
139 // tell service about id, shared memory and count
140 gl
->helper()->BeginQueryEXT(target(), id(), shm_id(), shm_offset());
145 void QueryTracker::Query::End(GLES2Implementation
* gl
) {
147 case GL_GET_ERROR_QUERY_CHROMIUM
: {
148 GLenum error
= gl
->GetClientSideGLError();
149 if (error
== GL_NO_ERROR
) {
150 // There was no error so start the query on the service.
151 // it will end immediately.
152 gl
->helper()->BeginQueryEXT(target(), id(), shm_id(), shm_offset());
154 // There's an error on the client, no need to bother the service. Just
155 // set the query as completed and return the error.
156 if (error
!= GL_NO_ERROR
) {
164 flush_count_
= gl
->helper()->flush_generation();
165 gl
->helper()->EndQueryEXT(target(), submit_count());
166 MarkAsPending(gl
->helper()->InsertToken());
169 void QueryTracker::Query::QueryCounter(GLES2Implementation
* gl
) {
171 flush_count_
= gl
->helper()->flush_generation();
172 gl
->helper()->QueryCounterEXT(target(), id(), shm_id(), shm_offset(),
174 MarkAsPending(gl
->helper()->InsertToken());
177 bool QueryTracker::Query::CheckResultsAvailable(
178 CommandBufferHelper
* helper
) {
180 if (base::subtle::Acquire_Load(&info_
.sync
->process_count
) ==
182 helper
->IsContextLost()) {
184 case GL_COMMANDS_ISSUED_CHROMIUM
:
185 result_
= info_
.sync
->result
;
187 case GL_LATENCY_QUERY_CHROMIUM
:
188 // Disabled DCHECK because of http://crbug.com/419236.
189 //DCHECK(info_.sync->result >= client_begin_time_us_);
190 result_
= info_
.sync
->result
- client_begin_time_us_
;
192 case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM
:
193 case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM
:
195 result_
= info_
.sync
->result
;
200 if ((helper
->flush_generation() - flush_count_
- 1) >= 0x80000000) {
203 // Insert no-ops so that eventually the GPU process will see more work.
208 return state_
== kComplete
;
211 uint64
QueryTracker::Query::GetResult() const {
212 DCHECK(state_
== kComplete
|| state_
== kUninitialized
);
216 QueryTracker::QueryTracker(MappedMemoryManager
* manager
)
217 : query_sync_manager_(manager
),
218 mapped_memory_(manager
),
219 disjoint_count_sync_shm_id_(-1),
220 disjoint_count_sync_shm_offset_(0),
221 disjoint_count_sync_(nullptr),
222 local_disjoint_count_(0) {
225 QueryTracker::~QueryTracker() {
226 while (!queries_
.empty()) {
227 delete queries_
.begin()->second
;
228 queries_
.erase(queries_
.begin());
230 while (!removed_queries_
.empty()) {
231 delete removed_queries_
.front();
232 removed_queries_
.pop_front();
234 if (disjoint_count_sync_
) {
235 mapped_memory_
->Free(disjoint_count_sync_
);
236 disjoint_count_sync_
= nullptr;
240 QueryTracker::Query
* QueryTracker::CreateQuery(GLuint id
, GLenum target
) {
242 FreeCompletedQueries();
243 QuerySyncManager::QueryInfo info
;
244 if (!query_sync_manager_
.Alloc(&info
)) {
247 Query
* query
= new Query(id
, target
, info
);
248 std::pair
<QueryIdMap::iterator
, bool> result
=
249 queries_
.insert(std::make_pair(id
, query
));
250 DCHECK(result
.second
);
254 QueryTracker::Query
* QueryTracker::GetQuery(GLuint client_id
) {
255 QueryIdMap::iterator it
= queries_
.find(client_id
);
256 return it
!= queries_
.end() ? it
->second
: nullptr;
259 QueryTracker::Query
* QueryTracker::GetCurrentQuery(GLenum target
) {
260 QueryTargetMap::iterator it
= current_queries_
.find(target
);
261 return it
!= current_queries_
.end() ? it
->second
: nullptr;
264 void QueryTracker::RemoveQuery(GLuint client_id
) {
265 QueryIdMap::iterator it
= queries_
.find(client_id
);
266 if (it
!= queries_
.end()) {
267 Query
* query
= it
->second
;
269 // Erase from current targets map if it is the current target.
270 const GLenum target
= query
->target();
271 QueryTargetMap::iterator target_it
= current_queries_
.find(target
);
272 if (target_it
!= current_queries_
.end() && target_it
->second
== query
) {
273 current_queries_
.erase(target_it
);
276 // When you delete a query you can't mark its memory as unused until it's
278 // Note: If you don't do this you won't mess up the service but you will
280 removed_queries_
.push_back(query
);
282 FreeCompletedQueries();
286 void QueryTracker::Shrink() {
287 FreeCompletedQueries();
288 query_sync_manager_
.Shrink();
291 void QueryTracker::FreeCompletedQueries() {
292 QueryList::iterator it
= removed_queries_
.begin();
293 while (it
!= removed_queries_
.end()) {
295 if (query
->Pending() &&
296 base::subtle::Acquire_Load(&query
->info_
.sync
->process_count
) !=
297 query
->submit_count()) {
302 query_sync_manager_
.Free(query
->info_
);
303 it
= removed_queries_
.erase(it
);
308 bool QueryTracker::BeginQuery(GLuint id
, GLenum target
,
309 GLES2Implementation
* gl
) {
310 QueryTracker::Query
* query
= GetQuery(id
);
312 query
= CreateQuery(id
, target
);
314 gl
->SetGLError(GL_OUT_OF_MEMORY
,
316 "transfer buffer allocation failed");
319 } else if (query
->target() != target
) {
320 gl
->SetGLError(GL_INVALID_OPERATION
,
322 "target does not match");
326 current_queries_
[query
->target()] = query
;
331 bool QueryTracker::EndQuery(GLenum target
, GLES2Implementation
* gl
) {
332 QueryTargetMap::iterator target_it
= current_queries_
.find(target
);
333 if (target_it
== current_queries_
.end()) {
334 gl
->SetGLError(GL_INVALID_OPERATION
,
335 "glEndQueryEXT", "no active query");
339 target_it
->second
->End(gl
);
340 current_queries_
.erase(target_it
);
344 bool QueryTracker::QueryCounter(GLuint id
, GLenum target
,
345 GLES2Implementation
* gl
) {
346 QueryTracker::Query
* query
= GetQuery(id
);
348 query
= CreateQuery(id
, target
);
350 gl
->SetGLError(GL_OUT_OF_MEMORY
,
352 "transfer buffer allocation failed");
355 } else if (query
->target() != target
) {
356 gl
->SetGLError(GL_INVALID_OPERATION
,
358 "target does not match");
362 query
->QueryCounter(gl
);
366 bool QueryTracker::SetDisjointSync(GLES2Implementation
* gl
) {
367 if (!disjoint_count_sync_
) {
368 // Allocate memory for disjoint value sync.
371 void* mem
= mapped_memory_
->Alloc(sizeof(*disjoint_count_sync_
),
375 disjoint_count_sync_shm_id_
= shm_id
;
376 disjoint_count_sync_shm_offset_
= shm_offset
;
377 disjoint_count_sync_
= static_cast<DisjointValueSync
*>(mem
);
378 disjoint_count_sync_
->Reset();
379 gl
->helper()->SetDisjointValueSyncCHROMIUM(shm_id
, shm_offset
);
382 return disjoint_count_sync_
!= nullptr;
385 bool QueryTracker::CheckAndResetDisjoint() {
386 if (disjoint_count_sync_
) {
387 const uint32_t disjoint_count
= disjoint_count_sync_
->GetDisjointCount();
388 if (local_disjoint_count_
!= disjoint_count
) {
389 local_disjoint_count_
= disjoint_count
;