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