• 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     // This doesn't do anything for D3D11 as we don't support timestamps
85     ASSERT(getType() == gl::QueryType::Timestamp);
86     mResultSum = 0;
87     mPendingQueries.push_back(std::unique_ptr<QueryState>(new QueryState()));
88     return angle::Result::Continue;
89 }
90 
91 template <typename T>
getResultBase(Context11 * context11,T * params)92 angle::Result Query11::getResultBase(Context11 *context11, T *params)
93 {
94     ASSERT(!mActiveQuery->query.valid());
95     ANGLE_TRY(flush(context11, true));
96     ASSERT(mPendingQueries.empty());
97     *params = static_cast<T>(mResultSum);
98 
99     return angle::Result::Continue;
100 }
101 
getResult(const gl::Context * context,GLint * params)102 angle::Result Query11::getResult(const gl::Context *context, GLint *params)
103 {
104     return getResultBase(GetImplAs<Context11>(context), params);
105 }
106 
getResult(const gl::Context * context,GLuint * params)107 angle::Result Query11::getResult(const gl::Context *context, GLuint *params)
108 {
109     return getResultBase(GetImplAs<Context11>(context), params);
110 }
111 
getResult(const gl::Context * context,GLint64 * params)112 angle::Result Query11::getResult(const gl::Context *context, GLint64 *params)
113 {
114     return getResultBase(GetImplAs<Context11>(context), params);
115 }
116 
getResult(const gl::Context * context,GLuint64 * params)117 angle::Result Query11::getResult(const gl::Context *context, GLuint64 *params)
118 {
119     return getResultBase(GetImplAs<Context11>(context), params);
120 }
121 
isResultAvailable(const gl::Context * context,bool * available)122 angle::Result Query11::isResultAvailable(const gl::Context *context, bool *available)
123 {
124     ANGLE_TRY(flush(GetImplAs<Context11>(context), false));
125 
126     *available = mPendingQueries.empty();
127     return angle::Result::Continue;
128 }
129 
pause(Context11 * context11)130 angle::Result Query11::pause(Context11 *context11)
131 {
132     if (mActiveQuery->query.valid())
133     {
134         ID3D11DeviceContext *context = mRenderer->getDeviceContext();
135         gl::QueryType type           = getType();
136 
137         // If we are doing time elapsed query the end timestamp
138         if (type == gl::QueryType::TimeElapsed)
139         {
140             context->End(mActiveQuery->endTimestamp.get());
141         }
142 
143         context->End(mActiveQuery->query.get());
144 
145         mPendingQueries.push_back(std::move(mActiveQuery));
146         mActiveQuery = std::unique_ptr<QueryState>(new QueryState());
147     }
148 
149     return flush(context11, false);
150 }
151 
resume(Context11 * context11)152 angle::Result Query11::resume(Context11 *context11)
153 {
154     if (!mActiveQuery->query.valid())
155     {
156         ANGLE_TRY(flush(context11, false));
157 
158         gl::QueryType type       = getType();
159         D3D11_QUERY d3dQueryType = gl_d3d11::ConvertQueryType(type);
160 
161         D3D11_QUERY_DESC queryDesc;
162         queryDesc.Query     = d3dQueryType;
163         queryDesc.MiscFlags = 0;
164 
165         ANGLE_TRY(mRenderer->allocateResource(context11, queryDesc, &mActiveQuery->query));
166 
167         // If we are doing time elapsed we also need a query to actually query the timestamp
168         if (type == gl::QueryType::TimeElapsed)
169         {
170             D3D11_QUERY_DESC desc;
171             desc.Query     = D3D11_QUERY_TIMESTAMP;
172             desc.MiscFlags = 0;
173 
174             ANGLE_TRY(mRenderer->allocateResource(context11, desc, &mActiveQuery->beginTimestamp));
175             ANGLE_TRY(mRenderer->allocateResource(context11, desc, &mActiveQuery->endTimestamp));
176         }
177 
178         ID3D11DeviceContext *context = mRenderer->getDeviceContext();
179 
180         if (d3dQueryType != D3D11_QUERY_EVENT)
181         {
182             context->Begin(mActiveQuery->query.get());
183         }
184 
185         // If we are doing time elapsed, query the begin timestamp
186         if (type == gl::QueryType::TimeElapsed)
187         {
188             context->End(mActiveQuery->beginTimestamp.get());
189         }
190     }
191 
192     return angle::Result::Continue;
193 }
194 
flush(Context11 * context11,bool force)195 angle::Result Query11::flush(Context11 *context11, bool force)
196 {
197     while (!mPendingQueries.empty())
198     {
199         QueryState *query = mPendingQueries.front().get();
200 
201         do
202         {
203             ANGLE_TRY(testQuery(context11, query));
204             if (!query->finished && !force)
205             {
206                 return angle::Result::Continue;
207             }
208         } while (!query->finished);
209 
210         mResultSum = MergeQueryResults(getType(), mResultSum, mResult);
211         mPendingQueries.pop_front();
212     }
213 
214     return angle::Result::Continue;
215 }
216 
testQuery(Context11 * context11,QueryState * queryState)217 angle::Result Query11::testQuery(Context11 *context11, QueryState *queryState)
218 {
219     if (!queryState->finished)
220     {
221         ID3D11DeviceContext *context = mRenderer->getDeviceContext();
222         switch (getType())
223         {
224             case gl::QueryType::AnySamples:
225             case gl::QueryType::AnySamplesConservative:
226             {
227                 ASSERT(queryState->query.valid());
228                 UINT64 numPixels = 0;
229                 HRESULT result =
230                     context->GetData(queryState->query.get(), &numPixels, sizeof(numPixels), 0);
231                 ANGLE_TRY_HR(context11, result, "Failed to get the data of an internal query");
232 
233                 if (result == S_OK)
234                 {
235                     queryState->finished = true;
236                     mResult              = (numPixels > 0) ? GL_TRUE : GL_FALSE;
237                 }
238             }
239             break;
240 
241             case gl::QueryType::TransformFeedbackPrimitivesWritten:
242             {
243                 ASSERT(queryState->query.valid());
244                 D3D11_QUERY_DATA_SO_STATISTICS soStats = {};
245                 HRESULT result =
246                     context->GetData(queryState->query.get(), &soStats, sizeof(soStats), 0);
247                 ANGLE_TRY_HR(context11, result, "Failed to get the data of an internal query");
248 
249                 if (result == S_OK)
250                 {
251                     queryState->finished = true;
252                     mResult              = static_cast<GLuint64>(soStats.NumPrimitivesWritten);
253                 }
254             }
255             break;
256 
257             case gl::QueryType::TimeElapsed:
258             {
259                 ASSERT(queryState->query.valid());
260                 ASSERT(queryState->beginTimestamp.valid());
261                 ASSERT(queryState->endTimestamp.valid());
262                 D3D11_QUERY_DATA_TIMESTAMP_DISJOINT timeStats = {};
263                 HRESULT result =
264                     context->GetData(queryState->query.get(), &timeStats, sizeof(timeStats), 0);
265                 ANGLE_TRY_HR(context11, result, "Failed to get the data of an internal query");
266 
267                 if (result == S_OK)
268                 {
269                     UINT64 beginTime = 0;
270                     HRESULT beginRes = context->GetData(queryState->beginTimestamp.get(),
271                                                         &beginTime, sizeof(UINT64), 0);
272                     ANGLE_TRY_HR(context11, beginRes,
273                                  "Failed to get the data of an internal query");
274 
275                     UINT64 endTime = 0;
276                     HRESULT endRes = context->GetData(queryState->endTimestamp.get(), &endTime,
277                                                       sizeof(UINT64), 0);
278                     ANGLE_TRY_HR(context11, endRes, "Failed to get the data of an internal query");
279 
280                     if (beginRes == S_OK && endRes == S_OK)
281                     {
282                         queryState->finished = true;
283                         if (timeStats.Disjoint)
284                         {
285                             mRenderer->setGPUDisjoint();
286                         }
287                         static_assert(sizeof(UINT64) == sizeof(unsigned long long),
288                                       "D3D UINT64 isn't 64 bits");
289 
290                         angle::CheckedNumeric<UINT64> checkedTime(endTime);
291                         checkedTime -= beginTime;
292                         checkedTime *= 1000000000ull;
293                         checkedTime /= timeStats.Frequency;
294                         if (checkedTime.IsValid())
295                         {
296                             mResult = checkedTime.ValueOrDie();
297                         }
298                         else
299                         {
300                             mResult = std::numeric_limits<GLuint64>::max() / timeStats.Frequency;
301                             // If an overflow does somehow occur, there is no way the elapsed time
302                             // is accurate, so we generate a disjoint event
303                             mRenderer->setGPUDisjoint();
304                         }
305                     }
306                 }
307             }
308             break;
309 
310             case gl::QueryType::Timestamp:
311             {
312                 // D3D11 doesn't support GL timestamp queries as D3D timestamps are not guaranteed
313                 // to have any sort of continuity outside of a disjoint timestamp query block, which
314                 // GL depends on
315                 ASSERT(!queryState->query.valid());
316                 mResult              = 0;
317                 queryState->finished = true;
318             }
319             break;
320 
321             case gl::QueryType::CommandsCompleted:
322             {
323                 ASSERT(queryState->query.valid());
324                 BOOL completed = 0;
325                 HRESULT result =
326                     context->GetData(queryState->query.get(), &completed, sizeof(completed), 0);
327                 ANGLE_TRY_HR(context11, result, "Failed to get the data of an internal query");
328 
329                 if (result == S_OK)
330                 {
331                     queryState->finished = true;
332                     ASSERT(completed == TRUE);
333                     mResult = (completed == TRUE) ? GL_TRUE : GL_FALSE;
334                 }
335             }
336             break;
337 
338             default:
339                 UNREACHABLE();
340                 break;
341         }
342 
343         queryState->getDataAttemptCount++;
344         bool checkDeviceLost =
345             (queryState->getDataAttemptCount % kPollingD3DDeviceLostCheckFrequency) == 0;
346         if (!queryState->finished && checkDeviceLost && mRenderer->testDeviceLost())
347         {
348             mRenderer->notifyDeviceLost();
349             ANGLE_TRY_HR(context11, E_OUTOFMEMORY,
350                          "Failed to test get query result, device is lost.");
351         }
352     }
353 
354     return angle::Result::Continue;
355 }
356 
357 }  // namespace rx
358