xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/d3d/d3d11/Query11.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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                                                                 &timestamp, 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