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