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