1 //
2 // Copyright 2016 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 // QueryVk.cpp:
7 // Implements the class methods for QueryVk.
8 //
9
10 #include "libANGLE/renderer/vulkan/QueryVk.h"
11 #include "libANGLE/Context.h"
12 #include "libANGLE/TransformFeedback.h"
13 #include "libANGLE/renderer/vulkan/ContextVk.h"
14 #include "libANGLE/renderer/vulkan/RendererVk.h"
15 #include "libANGLE/renderer/vulkan/TransformFeedbackVk.h"
16
17 #include "common/debug.h"
18
19 namespace rx
20 {
21
QueryVk(gl::QueryType type)22 QueryVk::QueryVk(gl::QueryType type)
23 : QueryImpl(type),
24 mTransformFeedbackPrimitivesDrawn(0),
25 mCachedResult(0),
26 mCachedResultValid(false)
27 {}
28
29 QueryVk::~QueryVk() = default;
30
onDestroy(const gl::Context * context)31 void QueryVk::onDestroy(const gl::Context *context)
32 {
33 ContextVk *contextVk = vk::GetImpl(context);
34 if (getType() != gl::QueryType::TransformFeedbackPrimitivesWritten)
35 {
36 vk::DynamicQueryPool *queryPool = contextVk->getQueryPool(getType());
37 queryPool->freeQuery(contextVk, &mQueryHelper);
38 queryPool->freeQuery(contextVk, &mQueryHelperTimeElapsedBegin);
39 }
40 }
41
stashQueryHelper(ContextVk * contextVk)42 angle::Result QueryVk::stashQueryHelper(ContextVk *contextVk)
43 {
44 ASSERT(isOcclusionQuery());
45 mStashedQueryHelpers.emplace_back(mQueryHelper);
46 mQueryHelper.deinit();
47 ANGLE_TRY(contextVk->getQueryPool(getType())->allocateQuery(contextVk, &mQueryHelper));
48 return angle::Result::Continue;
49 }
50
retrieveStashedQueryResult(ContextVk * contextVk,uint64_t * result)51 angle::Result QueryVk::retrieveStashedQueryResult(ContextVk *contextVk, uint64_t *result)
52 {
53 uint64_t total = 0;
54 for (vk::QueryHelper &query : mStashedQueryHelpers)
55 {
56 uint64_t v;
57 ANGLE_TRY(query.getUint64Result(contextVk, &v));
58 total += v;
59 }
60 mStashedQueryHelpers.clear();
61 *result = total;
62 return angle::Result::Continue;
63 }
64
begin(const gl::Context * context)65 angle::Result QueryVk::begin(const gl::Context *context)
66 {
67 ContextVk *contextVk = vk::GetImpl(context);
68
69 mCachedResultValid = false;
70
71 // Transform feedback query is a handled by a CPU-calculated value when emulated.
72 if (getType() == gl::QueryType::TransformFeedbackPrimitivesWritten)
73 {
74 mTransformFeedbackPrimitivesDrawn = 0;
75 // We could consider using VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT.
76 return angle::Result::Continue;
77 }
78
79 if (!mQueryHelper.valid())
80 {
81 ANGLE_TRY(contextVk->getQueryPool(getType())->allocateQuery(contextVk, &mQueryHelper));
82 }
83
84 if (isOcclusionQuery())
85 {
86 // For pathological usage case where begin/end is called back to back without flush and get
87 // result, we have to force flush so that the same mQueryHelper will not encoded in the same
88 // renderpass twice without resetting it.
89 if (mQueryHelper.hasPendingWork(contextVk))
90 {
91 ANGLE_TRY(contextVk->flushImpl(nullptr));
92 // As soon as beginQuery is called, previous query's result will not retrievable by API.
93 // We must clear it so that it will not count against current beginQuery call.
94 mStashedQueryHelpers.clear();
95 mQueryHelper.deinit();
96 ANGLE_TRY(contextVk->getQueryPool(getType())->allocateQuery(contextVk, &mQueryHelper));
97 }
98 contextVk->beginOcclusionQuery(this);
99 }
100 else if (getType() == gl::QueryType::TimeElapsed)
101 {
102 // Note: TimeElapsed is implemented by using two Timestamp queries and taking the diff.
103 if (!mQueryHelperTimeElapsedBegin.valid())
104 {
105 ANGLE_TRY(contextVk->getQueryPool(getType())->allocateQuery(
106 contextVk, &mQueryHelperTimeElapsedBegin));
107 }
108
109 ANGLE_TRY(mQueryHelperTimeElapsedBegin.flushAndWriteTimestamp(contextVk));
110 }
111 else
112 {
113 ANGLE_TRY(mQueryHelper.beginQuery(contextVk));
114 }
115
116 return angle::Result::Continue;
117 }
118
end(const gl::Context * context)119 angle::Result QueryVk::end(const gl::Context *context)
120 {
121 ContextVk *contextVk = vk::GetImpl(context);
122
123 if (getType() == gl::QueryType::TransformFeedbackPrimitivesWritten)
124 {
125 mCachedResult = mTransformFeedbackPrimitivesDrawn;
126
127 // There could be transform feedback in progress, so add the primitives drawn so far from
128 // the current transform feedback object.
129 gl::TransformFeedback *transformFeedback =
130 context->getState().getCurrentTransformFeedback();
131 if (transformFeedback)
132 {
133 mCachedResult += transformFeedback->getPrimitivesDrawn();
134 }
135 mCachedResultValid = true;
136 // We could consider using VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT.
137 }
138 else if (isOcclusionQuery())
139 {
140 contextVk->endOcclusionQuery(this);
141 }
142 else if (getType() == gl::QueryType::TimeElapsed)
143 {
144 ANGLE_TRY(mQueryHelper.flushAndWriteTimestamp(contextVk));
145 }
146 else
147 {
148 ANGLE_TRY(mQueryHelper.endQuery(contextVk));
149 }
150
151 return angle::Result::Continue;
152 }
153
queryCounter(const gl::Context * context)154 angle::Result QueryVk::queryCounter(const gl::Context *context)
155 {
156 ASSERT(getType() == gl::QueryType::Timestamp);
157 ContextVk *contextVk = vk::GetImpl(context);
158
159 mCachedResultValid = false;
160
161 if (!mQueryHelper.valid())
162 {
163 ANGLE_TRY(contextVk->getQueryPool(getType())->allocateQuery(contextVk, &mQueryHelper));
164 }
165
166 return mQueryHelper.flushAndWriteTimestamp(contextVk);
167 }
168
getResult(const gl::Context * context,bool wait)169 angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
170 {
171 if (mCachedResultValid)
172 {
173 return angle::Result::Continue;
174 }
175
176 ContextVk *contextVk = vk::GetImpl(context);
177 RendererVk *renderer = contextVk->getRenderer();
178
179 // glGetQueryObject* requires an implicit flush of the command buffers to guarantee execution in
180 // finite time.
181 // Note regarding time-elapsed: end should have been called after begin, so flushing when end
182 // has pending work should flush begin too.
183 if (mQueryHelper.hasPendingWork(contextVk))
184 {
185 ANGLE_TRY(contextVk->flushImpl(nullptr));
186
187 ASSERT(!mQueryHelperTimeElapsedBegin.hasPendingWork(contextVk));
188 ASSERT(!mQueryHelper.hasPendingWork(contextVk));
189 }
190
191 // If the command buffer this query is being written to is still in flight, its reset command
192 // may not have been performed by the GPU yet. To avoid a race condition in this case, wait
193 // for the batch to finish first before querying (or return not-ready if not waiting).
194 ANGLE_TRY(contextVk->checkCompletedCommands());
195 if (contextVk->isSerialInUse(mQueryHelper.getStoredQueueSerial()))
196 {
197 if (!wait)
198 {
199 return angle::Result::Continue;
200 }
201 ANGLE_TRY(contextVk->finishToSerial(mQueryHelper.getStoredQueueSerial()));
202 }
203
204 if (wait)
205 {
206 ANGLE_TRY(mQueryHelper.getUint64Result(contextVk, &mCachedResult));
207 uint64_t v;
208 ANGLE_TRY(retrieveStashedQueryResult(contextVk, &v));
209 mCachedResult += v;
210 }
211 else
212 {
213 bool available = false;
214 ANGLE_TRY(mQueryHelper.getUint64ResultNonBlocking(contextVk, &mCachedResult, &available));
215 if (!available)
216 {
217 // If the results are not ready, do nothing. mCachedResultValid remains false.
218 return angle::Result::Continue;
219 }
220 uint64_t v;
221 ANGLE_TRY(retrieveStashedQueryResult(contextVk, &v));
222 mCachedResult += v;
223 }
224
225 double timestampPeriod = renderer->getPhysicalDeviceProperties().limits.timestampPeriod;
226
227 // Fix up the results to what OpenGL expects.
228 switch (getType())
229 {
230 case gl::QueryType::AnySamples:
231 case gl::QueryType::AnySamplesConservative:
232 // OpenGL query result in these cases is binary
233 mCachedResult = !!mCachedResult;
234 break;
235 case gl::QueryType::Timestamp:
236 mCachedResult = static_cast<uint64_t>(mCachedResult * timestampPeriod);
237 break;
238 case gl::QueryType::TimeElapsed:
239 {
240 uint64_t timeElapsedEnd = mCachedResult;
241
242 // Since the result of the end query of time-elapsed is already available, the
243 // result of begin query must be available too.
244 ANGLE_TRY(mQueryHelperTimeElapsedBegin.getUint64Result(contextVk, &mCachedResult));
245
246 mCachedResult = timeElapsedEnd - mCachedResult;
247 mCachedResult = static_cast<uint64_t>(mCachedResult * timestampPeriod);
248
249 break;
250 }
251 default:
252 UNREACHABLE();
253 break;
254 }
255
256 mCachedResultValid = true;
257 return angle::Result::Continue;
258 }
getResult(const gl::Context * context,GLint * params)259 angle::Result QueryVk::getResult(const gl::Context *context, GLint *params)
260 {
261 ANGLE_TRY(getResult(context, true));
262 *params = static_cast<GLint>(mCachedResult);
263 return angle::Result::Continue;
264 }
265
getResult(const gl::Context * context,GLuint * params)266 angle::Result QueryVk::getResult(const gl::Context *context, GLuint *params)
267 {
268 ANGLE_TRY(getResult(context, true));
269 *params = static_cast<GLuint>(mCachedResult);
270 return angle::Result::Continue;
271 }
272
getResult(const gl::Context * context,GLint64 * params)273 angle::Result QueryVk::getResult(const gl::Context *context, GLint64 *params)
274 {
275 ANGLE_TRY(getResult(context, true));
276 *params = static_cast<GLint64>(mCachedResult);
277 return angle::Result::Continue;
278 }
279
getResult(const gl::Context * context,GLuint64 * params)280 angle::Result QueryVk::getResult(const gl::Context *context, GLuint64 *params)
281 {
282 ANGLE_TRY(getResult(context, true));
283 *params = mCachedResult;
284 return angle::Result::Continue;
285 }
286
isResultAvailable(const gl::Context * context,bool * available)287 angle::Result QueryVk::isResultAvailable(const gl::Context *context, bool *available)
288 {
289 ANGLE_TRY(getResult(context, false));
290 *available = mCachedResultValid;
291
292 return angle::Result::Continue;
293 }
294
onTransformFeedbackEnd(const gl::Context * context)295 void QueryVk::onTransformFeedbackEnd(const gl::Context *context)
296 {
297 gl::TransformFeedback *transformFeedback = context->getState().getCurrentTransformFeedback();
298 ASSERT(transformFeedback);
299
300 mTransformFeedbackPrimitivesDrawn += transformFeedback->getPrimitivesDrawn();
301 }
302
303 } // namespace rx
304