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