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