1 //
2 // Copyright 2013 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 // Query11.cpp: Defines the rx::Query11 class which implements rx::QueryImpl.
8
9 #include "libANGLE/renderer/d3d/d3d11/Query11.h"
10
11 #include <GLES2/gl2ext.h>
12
13 #include "common/utilities.h"
14 #include "libANGLE/Context.h"
15 #include "libANGLE/renderer/d3d/d3d11/Context11.h"
16 #include "libANGLE/renderer/d3d/d3d11/Renderer11.h"
17 #include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"
18
19 namespace
20 {
21
MergeQueryResults(gl::QueryType type,GLuint64 currentResult,GLuint64 newResult)22 GLuint64 MergeQueryResults(gl::QueryType type, GLuint64 currentResult, GLuint64 newResult)
23 {
24 switch (type)
25 {
26 case gl::QueryType::AnySamples:
27 case gl::QueryType::AnySamplesConservative:
28 return (currentResult == GL_TRUE || newResult == GL_TRUE) ? GL_TRUE : GL_FALSE;
29
30 case gl::QueryType::TransformFeedbackPrimitivesWritten:
31 return currentResult + newResult;
32
33 case gl::QueryType::TimeElapsed:
34 return currentResult + newResult;
35
36 case gl::QueryType::Timestamp:
37 return newResult;
38
39 case gl::QueryType::CommandsCompleted:
40 return newResult;
41
42 default:
43 UNREACHABLE();
44 return 0;
45 }
46 }
47
48 } // anonymous namespace
49
50 namespace rx
51 {
52
QueryState()53 Query11::QueryState::QueryState()
54 : getDataAttemptCount(0), query(), beginTimestamp(), endTimestamp(), finished(false)
55 {}
56
~QueryState()57 Query11::QueryState::~QueryState() {}
58
Query11(Renderer11 * renderer,gl::QueryType type)59 Query11::Query11(Renderer11 *renderer, gl::QueryType type)
60 : QueryImpl(type), mResult(0), mResultSum(0), mRenderer(renderer)
61 {
62 mActiveQuery = std::unique_ptr<QueryState>(new QueryState());
63 }
64
~Query11()65 Query11::~Query11()
66 {
67 mRenderer->getStateManager()->onDeleteQueryObject(this);
68 }
69
begin(const gl::Context * context)70 angle::Result Query11::begin(const gl::Context *context)
71 {
72 mPendingQueries.clear();
73 mResultSum = 0;
74 mRenderer->getStateManager()->onBeginQuery(this);
75 return resume(GetImplAs<Context11>(context));
76 }
77
end(const gl::Context * context)78 angle::Result Query11::end(const gl::Context *context)
79 {
80 return pause(GetImplAs<Context11>(context));
81 }
82
queryCounter(const gl::Context * context)83 angle::Result Query11::queryCounter(const gl::Context *context)
84 {
85 ASSERT(getType() == gl::QueryType::Timestamp);
86 if (!mRenderer->getFeatures().enableTimestampQueries.enabled)
87 {
88 mResultSum = 0;
89 return angle::Result::Continue;
90 }
91
92 Context11 *context11 = GetImplAs<Context11>(context);
93
94 D3D11_QUERY_DESC queryDesc;
95 queryDesc.MiscFlags = 0;
96 queryDesc.Query = D3D11_QUERY_TIMESTAMP;
97
98 ANGLE_TRY(mRenderer->allocateResource(context11, queryDesc, &mActiveQuery->endTimestamp));
99
100 ANGLE_TRY(context11->checkDisjointQuery());
101 ID3D11DeviceContext *contextD3D11 = mRenderer->getDeviceContext();
102 if (context11->getDisjointFrequency() > 0)
103 {
104 contextD3D11->End(mActiveQuery->endTimestamp.get());
105 }
106 else
107 {
108 // If the frequency hasn't been cached, insert a disjoint query to get the frequency.
109 queryDesc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT;
110 ANGLE_TRY(mRenderer->allocateResource(context11, queryDesc, &mActiveQuery->query));
111 contextD3D11->Begin(mActiveQuery->query.get());
112 contextD3D11->End(mActiveQuery->endTimestamp.get());
113 contextD3D11->End(mActiveQuery->query.get());
114 }
115
116 mPendingQueries.push_back(std::move(mActiveQuery));
117 mActiveQuery = std::unique_ptr<QueryState>(new QueryState());
118 return angle::Result::Continue;
119 }
120
121 template <typename T>
getResultBase(Context11 * context11,T * params)122 angle::Result Query11::getResultBase(Context11 *context11, T *params)
123 {
124 ASSERT(!mActiveQuery->query.valid());
125 ANGLE_TRY(flush(context11, true));
126 ASSERT(mPendingQueries.empty());
127 *params = static_cast<T>(mResultSum);
128
129 return angle::Result::Continue;
130 }
131
getResult(const gl::Context * context,GLint * params)132 angle::Result Query11::getResult(const gl::Context *context, GLint *params)
133 {
134 return getResultBase(GetImplAs<Context11>(context), params);
135 }
136
getResult(const gl::Context * context,GLuint * params)137 angle::Result Query11::getResult(const gl::Context *context, GLuint *params)
138 {
139 return getResultBase(GetImplAs<Context11>(context), params);
140 }
141
getResult(const gl::Context * context,GLint64 * params)142 angle::Result Query11::getResult(const gl::Context *context, GLint64 *params)
143 {
144 return getResultBase(GetImplAs<Context11>(context), params);
145 }
146
getResult(const gl::Context * context,GLuint64 * params)147 angle::Result Query11::getResult(const gl::Context *context, GLuint64 *params)
148 {
149 return getResultBase(GetImplAs<Context11>(context), params);
150 }
151
isResultAvailable(const gl::Context * context,bool * available)152 angle::Result Query11::isResultAvailable(const gl::Context *context, bool *available)
153 {
154 ANGLE_TRY(flush(GetImplAs<Context11>(context), false));
155
156 *available = mPendingQueries.empty();
157 return angle::Result::Continue;
158 }
159
pause(Context11 * context11)160 angle::Result Query11::pause(Context11 *context11)
161 {
162 if (mActiveQuery->query.valid())
163 {
164 ID3D11DeviceContext *context = mRenderer->getDeviceContext();
165 gl::QueryType type = getType();
166
167 // If we are doing time elapsed query the end timestamp
168 if (type == gl::QueryType::TimeElapsed)
169 {
170 context->End(mActiveQuery->endTimestamp.get());
171 }
172
173 context->End(mActiveQuery->query.get());
174
175 mPendingQueries.push_back(std::move(mActiveQuery));
176 mActiveQuery = std::unique_ptr<QueryState>(new QueryState());
177 }
178
179 return flush(context11, false);
180 }
181
resume(Context11 * context11)182 angle::Result Query11::resume(Context11 *context11)
183 {
184 if (!mActiveQuery->query.valid())
185 {
186 ANGLE_TRY(flush(context11, false));
187
188 gl::QueryType type = getType();
189 D3D11_QUERY d3dQueryType = gl_d3d11::ConvertQueryType(type);
190
191 D3D11_QUERY_DESC queryDesc;
192 queryDesc.Query = d3dQueryType;
193 queryDesc.MiscFlags = 0;
194
195 ANGLE_TRY(mRenderer->allocateResource(context11, queryDesc, &mActiveQuery->query));
196
197 // If we are doing time elapsed we also need a query to actually query the timestamp
198 if (type == gl::QueryType::TimeElapsed)
199 {
200 D3D11_QUERY_DESC desc;
201 desc.Query = D3D11_QUERY_TIMESTAMP;
202 desc.MiscFlags = 0;
203
204 ANGLE_TRY(mRenderer->allocateResource(context11, desc, &mActiveQuery->beginTimestamp));
205 ANGLE_TRY(mRenderer->allocateResource(context11, desc, &mActiveQuery->endTimestamp));
206 }
207
208 ID3D11DeviceContext *context = mRenderer->getDeviceContext();
209
210 if (d3dQueryType != D3D11_QUERY_EVENT)
211 {
212 context->Begin(mActiveQuery->query.get());
213 }
214
215 // If we are doing time elapsed, query the begin timestamp
216 if (type == gl::QueryType::TimeElapsed)
217 {
218 context->End(mActiveQuery->beginTimestamp.get());
219 }
220 }
221
222 return angle::Result::Continue;
223 }
224
flush(Context11 * context11,bool force)225 angle::Result Query11::flush(Context11 *context11, bool force)
226 {
227 while (!mPendingQueries.empty())
228 {
229 QueryState *query = mPendingQueries.front().get();
230
231 do
232 {
233 ANGLE_TRY(testQuery(context11, query));
234 if (!query->finished && !force)
235 {
236 return angle::Result::Continue;
237 }
238 } while (!query->finished);
239
240 mResultSum = MergeQueryResults(getType(), mResultSum, mResult);
241 mPendingQueries.pop_front();
242 }
243
244 return angle::Result::Continue;
245 }
246
testQuery(Context11 * context11,QueryState * queryState)247 angle::Result Query11::testQuery(Context11 *context11, QueryState *queryState)
248 {
249 if (!queryState->finished)
250 {
251 ID3D11DeviceContext *context = mRenderer->getDeviceContext();
252 switch (getType())
253 {
254 case gl::QueryType::AnySamples:
255 case gl::QueryType::AnySamplesConservative:
256 {
257 ASSERT(queryState->query.valid());
258 UINT64 numPixels = 0;
259 HRESULT result =
260 context->GetData(queryState->query.get(), &numPixels, sizeof(numPixels), 0);
261 ANGLE_TRY_HR(context11, result, "Failed to get the data of an internal query");
262
263 if (result == S_OK)
264 {
265 queryState->finished = true;
266 mResult = (numPixels > 0) ? GL_TRUE : GL_FALSE;
267 }
268 }
269 break;
270
271 case gl::QueryType::TransformFeedbackPrimitivesWritten:
272 {
273 ASSERT(queryState->query.valid());
274 D3D11_QUERY_DATA_SO_STATISTICS soStats = {};
275 HRESULT result =
276 context->GetData(queryState->query.get(), &soStats, sizeof(soStats), 0);
277 ANGLE_TRY_HR(context11, result, "Failed to get the data of an internal query");
278
279 if (result == S_OK)
280 {
281 queryState->finished = true;
282 mResult = static_cast<GLuint64>(soStats.NumPrimitivesWritten);
283 }
284 }
285 break;
286
287 case gl::QueryType::TimeElapsed:
288 {
289 ASSERT(queryState->query.valid());
290 ASSERT(queryState->beginTimestamp.valid());
291 ASSERT(queryState->endTimestamp.valid());
292 D3D11_QUERY_DATA_TIMESTAMP_DISJOINT timeStats = {};
293 HRESULT result =
294 context->GetData(queryState->query.get(), &timeStats, sizeof(timeStats), 0);
295 ANGLE_TRY_HR(context11, result, "Failed to get the data of an internal query");
296
297 if (result == S_OK)
298 {
299 UINT64 beginTime = 0;
300 HRESULT beginRes = context->GetData(queryState->beginTimestamp.get(),
301 &beginTime, sizeof(UINT64), 0);
302 ANGLE_TRY_HR(context11, beginRes,
303 "Failed to get the data of an internal query");
304
305 UINT64 endTime = 0;
306 HRESULT endRes = context->GetData(queryState->endTimestamp.get(), &endTime,
307 sizeof(UINT64), 0);
308 ANGLE_TRY_HR(context11, endRes, "Failed to get the data of an internal query");
309
310 if (beginRes == S_OK && endRes == S_OK)
311 {
312 queryState->finished = true;
313 if (timeStats.Disjoint)
314 {
315 context11->setGPUDisjoint();
316 }
317 static_assert(sizeof(UINT64) == sizeof(unsigned long long),
318 "D3D UINT64 isn't 64 bits");
319
320 angle::CheckedNumeric<UINT64> checkedTime(endTime);
321 checkedTime -= beginTime;
322 checkedTime *= 1000000000ull;
323 checkedTime /= timeStats.Frequency;
324 if (checkedTime.IsValid())
325 {
326 mResult = checkedTime.ValueOrDie();
327 }
328 else
329 {
330 mResult = std::numeric_limits<GLuint64>::max() / timeStats.Frequency;
331 // If an overflow does somehow occur, there is no way the elapsed time
332 // is accurate, so we generate a disjoint event
333 context11->setGPUDisjoint();
334 }
335 }
336 }
337 }
338 break;
339
340 case gl::QueryType::Timestamp:
341 {
342 if (!mRenderer->getFeatures().enableTimestampQueries.enabled)
343 {
344 mResult = 0;
345 queryState->finished = true;
346 }
347 else
348 {
349 bool hasFrequency = context11->getDisjointFrequency() > 0;
350 HRESULT result = S_OK;
351 if (!hasFrequency)
352 {
353 ASSERT(queryState->query.valid());
354 D3D11_QUERY_DATA_TIMESTAMP_DISJOINT timeStats = {};
355 result = context->GetData(queryState->query.get(), &timeStats,
356 sizeof(timeStats), 0);
357 ANGLE_TRY_HR(context11, result,
358 "Failed to get the data of an internal query");
359 if (result == S_OK)
360 {
361 context11->setDisjointFrequency(timeStats.Frequency);
362 if (timeStats.Disjoint)
363 {
364 context11->setGPUDisjoint();
365 }
366 }
367 }
368 if (result == S_OK)
369 {
370 ASSERT(queryState->endTimestamp.valid());
371 UINT64 timestamp = 0;
372 HRESULT timestampRes = context->GetData(queryState->endTimestamp.get(),
373 ×tamp, sizeof(UINT64), 0);
374 ANGLE_TRY_HR(context11, timestampRes,
375 "Failed to get the data of an internal query");
376
377 if (timestampRes == S_OK)
378 {
379 ASSERT(context11->getDisjointFrequency() > 0);
380 queryState->finished = true;
381 static_assert(sizeof(UINT64) == sizeof(unsigned long long),
382 "D3D UINT64 isn't 64 bits");
383
384 timestamp = static_cast<uint64_t>(
385 timestamp *
386 (1000000000.0 /
387 static_cast<double>(context11->getDisjointFrequency())));
388
389 angle::CheckedNumeric<UINT64> checkedTime(timestamp);
390 if (checkedTime.IsValid())
391 {
392 mResult = checkedTime.ValueOrDie();
393 }
394 else
395 {
396 mResult = std::numeric_limits<GLuint64>::max();
397 // If an overflow does somehow occur, there is no way the elapsed
398 // time is accurate, so we generate a disjoint event
399 context11->setGPUDisjoint();
400 }
401 }
402 }
403 }
404 }
405 break;
406
407 case gl::QueryType::CommandsCompleted:
408 {
409 ASSERT(queryState->query.valid());
410 BOOL completed = 0;
411 HRESULT result =
412 context->GetData(queryState->query.get(), &completed, sizeof(completed), 0);
413 ANGLE_TRY_HR(context11, result, "Failed to get the data of an internal query");
414
415 if (result == S_OK)
416 {
417 queryState->finished = true;
418 ASSERT(completed == TRUE);
419 mResult = (completed == TRUE) ? GL_TRUE : GL_FALSE;
420 }
421 }
422 break;
423
424 default:
425 UNREACHABLE();
426 break;
427 }
428
429 queryState->getDataAttemptCount++;
430 bool checkDeviceLost =
431 (queryState->getDataAttemptCount % kPollingD3DDeviceLostCheckFrequency) == 0;
432 if (!queryState->finished && checkDeviceLost && mRenderer->testDeviceLost())
433 {
434 mRenderer->notifyDeviceLost();
435 ANGLE_TRY_HR(context11, E_OUTOFMEMORY,
436 "Failed to test get query result, device is lost.");
437 }
438 }
439
440 return angle::Result::Continue;
441 }
442
443 } // namespace rx
444