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