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