1 //
2 // Copyright 2015 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // QueryGL.cpp: Implements the class methods for QueryGL.
8
9 #include "libANGLE/renderer/gl/QueryGL.h"
10
11 #include "common/debug.h"
12 #include "libANGLE/Context.h"
13 #include "libANGLE/renderer/gl/ContextGL.h"
14 #include "libANGLE/renderer/gl/FunctionsGL.h"
15 #include "libANGLE/renderer/gl/StateManagerGL.h"
16 #include "libANGLE/renderer/gl/renderergl_utils.h"
17
18 namespace
19 {
20
MergeQueryResults(gl::QueryType type,GLuint64 currentResult,GLuint64 newResult)21 GLuint64 MergeQueryResults(gl::QueryType type, GLuint64 currentResult, GLuint64 newResult)
22 {
23 switch (type)
24 {
25 case gl::QueryType::AnySamples:
26 case gl::QueryType::AnySamplesConservative:
27 return (currentResult == GL_TRUE || newResult == GL_TRUE) ? GL_TRUE : GL_FALSE;
28
29 case gl::QueryType::TransformFeedbackPrimitivesWritten:
30 return currentResult + newResult;
31
32 case gl::QueryType::TimeElapsed:
33 return currentResult + newResult;
34
35 case gl::QueryType::Timestamp:
36 return newResult;
37
38 case gl::QueryType::PrimitivesGenerated:
39 return currentResult + newResult;
40
41 default:
42 UNREACHABLE();
43 return 0;
44 }
45 }
46
47 } // anonymous namespace
48
49 namespace rx
50 {
51
QueryGL(gl::QueryType type)52 QueryGL::QueryGL(gl::QueryType type) : QueryImpl(type) {}
53
~QueryGL()54 QueryGL::~QueryGL() {}
55
StandardQueryGL(gl::QueryType type,const FunctionsGL * functions,StateManagerGL * stateManager)56 StandardQueryGL::StandardQueryGL(gl::QueryType type,
57 const FunctionsGL *functions,
58 StateManagerGL *stateManager)
59 : QueryGL(type),
60 mType(type),
61 mFunctions(functions),
62 mStateManager(stateManager),
63 mActiveQuery(0),
64 mPendingQueries(),
65 mResultSum(0)
66 {}
67
~StandardQueryGL()68 StandardQueryGL::~StandardQueryGL()
69 {
70 if (mActiveQuery != 0)
71 {
72 mStateManager->endQuery(mType, this, mActiveQuery);
73 mFunctions->deleteQueries(1, &mActiveQuery);
74 mActiveQuery = 0;
75 }
76
77 while (!mPendingQueries.empty())
78 {
79 GLuint id = mPendingQueries.front();
80 mFunctions->deleteQueries(1, &id);
81 mPendingQueries.pop_front();
82 }
83 }
84
begin(const gl::Context * context)85 angle::Result StandardQueryGL::begin(const gl::Context *context)
86 {
87 mResultSum = 0;
88 return resume(context);
89 }
90
end(const gl::Context * context)91 angle::Result StandardQueryGL::end(const gl::Context *context)
92 {
93 return pause(context);
94 }
95
queryCounter(const gl::Context * context)96 angle::Result StandardQueryGL::queryCounter(const gl::Context *context)
97 {
98 ASSERT(mType == gl::QueryType::Timestamp);
99
100 // Directly create a query for the timestamp and add it to the pending query queue, as timestamp
101 // queries do not have the traditional begin/end block and never need to be paused/resumed
102 GLuint query;
103 mFunctions->genQueries(1, &query);
104 mFunctions->queryCounter(query, GL_TIMESTAMP);
105 mPendingQueries.push_back(query);
106
107 return angle::Result::Continue;
108 }
109
110 template <typename T>
getResultBase(const gl::Context * context,T * params)111 angle::Result StandardQueryGL::getResultBase(const gl::Context *context, T *params)
112 {
113 ASSERT(mActiveQuery == 0);
114
115 ANGLE_TRY(flush(context, true));
116 ASSERT(mPendingQueries.empty());
117 *params = static_cast<T>(mResultSum);
118
119 return angle::Result::Continue;
120 }
121
getResult(const gl::Context * context,GLint * params)122 angle::Result StandardQueryGL::getResult(const gl::Context *context, GLint *params)
123 {
124 return getResultBase(context, params);
125 }
126
getResult(const gl::Context * context,GLuint * params)127 angle::Result StandardQueryGL::getResult(const gl::Context *context, GLuint *params)
128 {
129 return getResultBase(context, params);
130 }
131
getResult(const gl::Context * context,GLint64 * params)132 angle::Result StandardQueryGL::getResult(const gl::Context *context, GLint64 *params)
133 {
134 return getResultBase(context, params);
135 }
136
getResult(const gl::Context * context,GLuint64 * params)137 angle::Result StandardQueryGL::getResult(const gl::Context *context, GLuint64 *params)
138 {
139 return getResultBase(context, params);
140 }
141
isResultAvailable(const gl::Context * context,bool * available)142 angle::Result StandardQueryGL::isResultAvailable(const gl::Context *context, bool *available)
143 {
144 ASSERT(mActiveQuery == 0);
145
146 ANGLE_TRY(flush(context, false));
147 *available = mPendingQueries.empty();
148 return angle::Result::Continue;
149 }
150
pause(const gl::Context * context)151 angle::Result StandardQueryGL::pause(const gl::Context *context)
152 {
153 if (mActiveQuery != 0)
154 {
155 mStateManager->endQuery(mType, this, mActiveQuery);
156
157 mPendingQueries.push_back(mActiveQuery);
158 mActiveQuery = 0;
159 }
160
161 // Flush to make sure the pending queries don't add up too much.
162 return flush(context, false);
163 }
164
resume(const gl::Context * context)165 angle::Result StandardQueryGL::resume(const gl::Context *context)
166 {
167 if (mActiveQuery == 0)
168 {
169 // Flush to make sure the pending queries don't add up too much.
170 ANGLE_TRY(flush(context, false));
171 mFunctions->genQueries(1, &mActiveQuery);
172 mStateManager->beginQuery(mType, this, mActiveQuery);
173 }
174
175 return angle::Result::Continue;
176 }
177
flush(const gl::Context * context,bool force)178 angle::Result StandardQueryGL::flush(const gl::Context *context, bool force)
179 {
180 while (!mPendingQueries.empty())
181 {
182 GLuint id = mPendingQueries.front();
183 if (!force)
184 {
185 GLuint resultAvailable = 0;
186 mFunctions->getQueryObjectuiv(id, GL_QUERY_RESULT_AVAILABLE, &resultAvailable);
187 if (resultAvailable == GL_FALSE)
188 {
189 return angle::Result::Continue;
190 }
191 }
192
193 // Even though getQueryObjectui64v was introduced for timer queries, there is nothing in the
194 // standard that says that it doesn't work for any other queries. It also passes on all the
195 // trybots, so we use it if it is available
196 if (mFunctions->getQueryObjectui64v != nullptr)
197 {
198 GLuint64 result = 0;
199 mFunctions->getQueryObjectui64v(id, GL_QUERY_RESULT, &result);
200 mResultSum = MergeQueryResults(mType, mResultSum, result);
201 }
202 else
203 {
204 GLuint result = 0;
205 mFunctions->getQueryObjectuiv(id, GL_QUERY_RESULT, &result);
206 mResultSum = MergeQueryResults(mType, mResultSum, static_cast<GLuint64>(result));
207 }
208
209 mFunctions->deleteQueries(1, &id);
210
211 mPendingQueries.pop_front();
212 }
213
214 return angle::Result::Continue;
215 }
216
217 class SyncProviderGL
218 {
219 public:
~SyncProviderGL()220 virtual ~SyncProviderGL() {}
init(const gl::Context * context,gl::QueryType queryType)221 virtual angle::Result init(const gl::Context *context, gl::QueryType queryType)
222 {
223 return angle::Result::Continue;
224 }
225 virtual angle::Result flush(const gl::Context *context, bool force, bool *finished) = 0;
226 };
227
228 class SyncProviderGLSync : public SyncProviderGL
229 {
230 public:
SyncProviderGLSync(const FunctionsGL * functions)231 SyncProviderGLSync(const FunctionsGL *functions) : mFunctions(functions), mSync(nullptr)
232 {
233 mSync = mFunctions->fenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
234 }
235
~SyncProviderGLSync()236 ~SyncProviderGLSync() override { mFunctions->deleteSync(mSync); }
237
flush(const gl::Context * context,bool force,bool * finished)238 angle::Result flush(const gl::Context *context, bool force, bool *finished) override
239 {
240 if (force)
241 {
242 mFunctions->clientWaitSync(mSync, 0, 0);
243 *finished = true;
244 }
245 else
246 {
247 GLint value = 0;
248 mFunctions->getSynciv(mSync, GL_SYNC_STATUS, 1, nullptr, &value);
249 *finished = (value == GL_SIGNALED);
250 }
251
252 return angle::Result::Continue;
253 }
254
255 private:
256 const FunctionsGL *mFunctions;
257 GLsync mSync;
258 };
259
260 class SyncProviderGLQuery : public SyncProviderGL
261 {
262 public:
SyncProviderGLQuery(const FunctionsGL * functions)263 SyncProviderGLQuery(const FunctionsGL *functions) : mFunctions(functions), mQuery(0) {}
264
init(const gl::Context * context,gl::QueryType type)265 angle::Result init(const gl::Context *context, gl::QueryType type) override
266 {
267 StateManagerGL *stateManager = GetStateManagerGL(context);
268
269 mFunctions->genQueries(1, &mQuery);
270 ANGLE_TRY(stateManager->pauseQuery(context, type));
271 mFunctions->beginQuery(ToGLenum(type), mQuery);
272 mFunctions->endQuery(ToGLenum(type));
273 return stateManager->resumeQuery(context, type);
274 }
275
~SyncProviderGLQuery()276 ~SyncProviderGLQuery() override { mFunctions->deleteQueries(1, &mQuery); }
277
flush(const gl::Context * context,bool force,bool * finished)278 angle::Result flush(const gl::Context *context, bool force, bool *finished) override
279 {
280 if (force)
281 {
282 GLint result = 0;
283 mFunctions->getQueryObjectiv(mQuery, GL_QUERY_RESULT, &result);
284 *finished = true;
285 }
286 else
287 {
288 GLint available = 0;
289 mFunctions->getQueryObjectiv(mQuery, GL_QUERY_RESULT_AVAILABLE, &available);
290 *finished = (available == GL_TRUE);
291 }
292
293 return angle::Result::Continue;
294 }
295
296 private:
297 const FunctionsGL *mFunctions;
298 GLuint mQuery;
299 };
300
SyncQueryGL(gl::QueryType type,const FunctionsGL * functions)301 SyncQueryGL::SyncQueryGL(gl::QueryType type, const FunctionsGL *functions)
302 : QueryGL(type), mFunctions(functions), mSyncProvider(nullptr), mFinished(false)
303 {
304 ASSERT(IsSupported(mFunctions));
305 ASSERT(type == gl::QueryType::CommandsCompleted);
306 }
307
~SyncQueryGL()308 SyncQueryGL::~SyncQueryGL() {}
309
IsSupported(const FunctionsGL * functions)310 bool SyncQueryGL::IsSupported(const FunctionsGL *functions)
311 {
312 return nativegl::SupportsFenceSync(functions) || nativegl::SupportsOcclusionQueries(functions);
313 }
314
begin(const gl::Context * context)315 angle::Result SyncQueryGL::begin(const gl::Context *context)
316 {
317 return angle::Result::Continue;
318 }
319
end(const gl::Context * context)320 angle::Result SyncQueryGL::end(const gl::Context *context)
321 {
322 if (nativegl::SupportsFenceSync(mFunctions))
323 {
324 mSyncProvider.reset(new SyncProviderGLSync(mFunctions));
325 }
326 else if (nativegl::SupportsOcclusionQueries(mFunctions))
327 {
328 mSyncProvider.reset(new SyncProviderGLQuery(mFunctions));
329 ANGLE_TRY(mSyncProvider->init(context, gl::QueryType::AnySamples));
330 }
331 else
332 {
333 ANGLE_GL_UNREACHABLE(GetImplAs<ContextGL>(context));
334 }
335 return angle::Result::Continue;
336 }
337
queryCounter(const gl::Context * context)338 angle::Result SyncQueryGL::queryCounter(const gl::Context *context)
339 {
340 UNREACHABLE();
341 return angle::Result::Continue;
342 }
343
getResult(const gl::Context * context,GLint * params)344 angle::Result SyncQueryGL::getResult(const gl::Context *context, GLint *params)
345 {
346 return getResultBase(context, params);
347 }
348
getResult(const gl::Context * context,GLuint * params)349 angle::Result SyncQueryGL::getResult(const gl::Context *context, GLuint *params)
350 {
351 return getResultBase(context, params);
352 }
353
getResult(const gl::Context * context,GLint64 * params)354 angle::Result SyncQueryGL::getResult(const gl::Context *context, GLint64 *params)
355 {
356 return getResultBase(context, params);
357 }
358
getResult(const gl::Context * context,GLuint64 * params)359 angle::Result SyncQueryGL::getResult(const gl::Context *context, GLuint64 *params)
360 {
361 return getResultBase(context, params);
362 }
363
isResultAvailable(const gl::Context * context,bool * available)364 angle::Result SyncQueryGL::isResultAvailable(const gl::Context *context, bool *available)
365 {
366 ANGLE_TRY(flush(context, false));
367 *available = mFinished;
368 return angle::Result::Continue;
369 }
370
pause(const gl::Context * context)371 angle::Result SyncQueryGL::pause(const gl::Context *context)
372 {
373 return angle::Result::Continue;
374 }
375
resume(const gl::Context * context)376 angle::Result SyncQueryGL::resume(const gl::Context *context)
377 {
378 return angle::Result::Continue;
379 }
380
flush(const gl::Context * context,bool force)381 angle::Result SyncQueryGL::flush(const gl::Context *context, bool force)
382 {
383 if (mSyncProvider == nullptr)
384 {
385 ASSERT(mFinished);
386 return angle::Result::Continue;
387 }
388
389 ANGLE_TRY(mSyncProvider->flush(context, force, &mFinished));
390 if (mFinished)
391 {
392 mSyncProvider.reset();
393 }
394
395 return angle::Result::Continue;
396 }
397
398 template <typename T>
getResultBase(const gl::Context * context,T * params)399 angle::Result SyncQueryGL::getResultBase(const gl::Context *context, T *params)
400 {
401 ANGLE_TRY(flush(context, true));
402 *params = static_cast<T>(mFinished ? GL_TRUE : GL_FALSE);
403 return angle::Result::Continue;
404 }
405 } // namespace rx
406