• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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