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/renderer/gl/FunctionsGL.h"
13 #include "libANGLE/renderer/gl/StateManagerGL.h"
14
15 namespace
16 {
17
MergeQueryResults(GLenum type,GLuint64 currentResult,GLuint64 newResult)18 GLuint64 MergeQueryResults(GLenum type, GLuint64 currentResult, GLuint64 newResult)
19 {
20 switch (type)
21 {
22 case GL_ANY_SAMPLES_PASSED:
23 case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
24 return (currentResult == GL_TRUE || newResult == GL_TRUE) ? GL_TRUE : GL_FALSE;
25
26 case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
27 return currentResult + newResult;
28
29 case GL_TIME_ELAPSED:
30 return currentResult + newResult;
31
32 case GL_TIMESTAMP:
33 return newResult;
34
35 default:
36 UNREACHABLE();
37 return 0;
38 }
39 }
40
41 } // anonymous namespace
42
43 namespace rx
44 {
45
QueryGL(GLenum type,const FunctionsGL * functions,StateManagerGL * stateManager)46 QueryGL::QueryGL(GLenum type, const FunctionsGL *functions, StateManagerGL *stateManager)
47 : QueryImpl(type),
48 mType(type),
49 mFunctions(functions),
50 mStateManager(stateManager),
51 mActiveQuery(0),
52 mPendingQueries(),
53 mResultSum(0)
54 {
55 }
56
~QueryGL()57 QueryGL::~QueryGL()
58 {
59 mStateManager->deleteQuery(mActiveQuery);
60 mStateManager->onDeleteQueryObject(this);
61 while (!mPendingQueries.empty())
62 {
63 mStateManager->deleteQuery(mPendingQueries.front());
64 mPendingQueries.pop_front();
65 }
66 }
67
begin()68 gl::Error QueryGL::begin()
69 {
70 mResultSum = 0;
71 mStateManager->onBeginQuery(this);
72 return resume();
73 }
74
end()75 gl::Error QueryGL::end()
76 {
77 return pause();
78 }
79
queryCounter()80 gl::Error QueryGL::queryCounter()
81 {
82 ASSERT(mType == GL_TIMESTAMP);
83
84 // Directly create a query for the timestamp and add it to the pending query queue, as timestamp
85 // queries do not have the traditional begin/end block and never need to be paused/resumed
86 GLuint query;
87 mFunctions->genQueries(1, &query);
88 mFunctions->queryCounter(query, GL_TIMESTAMP);
89 mPendingQueries.push_back(query);
90
91 return gl::Error(GL_NO_ERROR);
92 }
93
94 template <typename T>
getResultBase(T * params)95 gl::Error QueryGL::getResultBase(T *params)
96 {
97 ASSERT(mActiveQuery == 0);
98
99 gl::Error error = flush(true);
100 if (error.isError())
101 {
102 return error;
103 }
104
105 ASSERT(mPendingQueries.empty());
106 *params = static_cast<T>(mResultSum);
107
108 return gl::Error(GL_NO_ERROR);
109 }
110
getResult(GLint * params)111 gl::Error QueryGL::getResult(GLint *params)
112 {
113 return getResultBase(params);
114 }
115
getResult(GLuint * params)116 gl::Error QueryGL::getResult(GLuint *params)
117 {
118 return getResultBase(params);
119 }
120
getResult(GLint64 * params)121 gl::Error QueryGL::getResult(GLint64 *params)
122 {
123 return getResultBase(params);
124 }
125
getResult(GLuint64 * params)126 gl::Error QueryGL::getResult(GLuint64 *params)
127 {
128 return getResultBase(params);
129 }
130
isResultAvailable(bool * available)131 gl::Error QueryGL::isResultAvailable(bool *available)
132 {
133 ASSERT(mActiveQuery == 0);
134
135 gl::Error error = flush(false);
136 if (error.isError())
137 {
138 return error;
139 }
140
141 *available = mPendingQueries.empty();
142 return gl::Error(GL_NO_ERROR);
143 }
144
pause()145 gl::Error QueryGL::pause()
146 {
147 if (mActiveQuery != 0)
148 {
149 mStateManager->endQuery(mType, mActiveQuery);
150
151 mPendingQueries.push_back(mActiveQuery);
152 mActiveQuery = 0;
153 }
154
155 // Flush to make sure the pending queries don't add up too much.
156 gl::Error error = flush(false);
157 if (error.isError())
158 {
159 return error;
160 }
161
162 return gl::Error(GL_NO_ERROR);
163 }
164
resume()165 gl::Error QueryGL::resume()
166 {
167 if (mActiveQuery == 0)
168 {
169 // Flush to make sure the pending queries don't add up too much.
170 gl::Error error = flush(false);
171 if (error.isError())
172 {
173 return error;
174 }
175
176 mFunctions->genQueries(1, &mActiveQuery);
177 mStateManager->beginQuery(mType, mActiveQuery);
178 }
179
180 return gl::Error(GL_NO_ERROR);
181 }
182
flush(bool force)183 gl::Error QueryGL::flush(bool force)
184 {
185 while (!mPendingQueries.empty())
186 {
187 GLuint id = mPendingQueries.front();
188 if (!force)
189 {
190 GLuint resultAvailable = 0;
191 mFunctions->getQueryObjectuiv(id, GL_QUERY_RESULT_AVAILABLE, &resultAvailable);
192 if (resultAvailable == GL_FALSE)
193 {
194 return gl::Error(GL_NO_ERROR);
195 }
196 }
197
198 // Even though getQueryObjectui64v was introduced for timer queries, there is nothing in the
199 // standard that says that it doesn't work for any other queries. It also passes on all the
200 // trybots, so we use it if it is available
201 if (mFunctions->getQueryObjectui64v != nullptr)
202 {
203 GLuint64 result = 0;
204 mFunctions->getQueryObjectui64v(id, GL_QUERY_RESULT, &result);
205 mResultSum = MergeQueryResults(mType, mResultSum, result);
206 }
207 else
208 {
209 GLuint result = 0;
210 mFunctions->getQueryObjectuiv(id, GL_QUERY_RESULT, &result);
211 mResultSum = MergeQueryResults(mType, mResultSum, static_cast<GLuint64>(result));
212 }
213
214 mStateManager->deleteQuery(id);
215
216 mPendingQueries.pop_front();
217 }
218
219 return gl::Error(GL_NO_ERROR);
220 }
221
222 }
223