1 //
2 // Copyright (c) 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/renderer/d3d/d3d11/Renderer11.h"
15 #include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"
16 
17 namespace
18 {
19 
MergeQueryResults(GLenum type,GLuint64 currentResult,GLuint64 newResult)20 GLuint64 MergeQueryResults(GLenum type, GLuint64 currentResult, GLuint64 newResult)
21 {
22     switch (type)
23     {
24         case GL_ANY_SAMPLES_PASSED:
25         case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
26             return (currentResult == GL_TRUE || newResult == GL_TRUE) ? GL_TRUE : GL_FALSE;
27 
28         case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
29             return currentResult + newResult;
30 
31         case GL_TIME_ELAPSED_EXT:
32             return currentResult + newResult;
33 
34         case GL_TIMESTAMP_EXT:
35             return newResult;
36 
37         case GL_COMMANDS_COMPLETED_CHROMIUM:
38             return newResult;
39 
40         default:
41             UNREACHABLE();
42             return 0;
43     }
44 }
45 
46 }  // anonymous namespace
47 
48 namespace rx
49 {
50 
QueryState()51 Query11::QueryState::QueryState() : query(), beginTimestamp(), endTimestamp(), finished(false)
52 {
53 }
54 
~QueryState()55 Query11::QueryState::~QueryState()
56 {
57 }
58 
Query11(Renderer11 * renderer,GLenum type)59 Query11::Query11(Renderer11 *renderer, GLenum 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()70 gl::Error Query11::begin()
71 {
72     mResultSum = 0;
73     mRenderer->getStateManager()->onBeginQuery(this);
74     return resume();
75 }
76 
end()77 gl::Error Query11::end()
78 {
79     return pause();
80 }
81 
queryCounter()82 gl::Error Query11::queryCounter()
83 {
84     // This doesn't do anything for D3D11 as we don't support timestamps
85     ASSERT(getType() == GL_TIMESTAMP_EXT);
86     mResultSum = 0;
87     mPendingQueries.push_back(std::unique_ptr<QueryState>(new QueryState()));
88     return gl::NoError();
89 }
90 
91 template <typename T>
getResultBase(T * params)92 gl::Error Query11::getResultBase(T *params)
93 {
94     ASSERT(!mActiveQuery->query.valid());
95     ANGLE_TRY(flush(true));
96     ASSERT(mPendingQueries.empty());
97     *params = static_cast<T>(mResultSum);
98 
99     return gl::NoError();
100 }
101 
getResult(GLint * params)102 gl::Error Query11::getResult(GLint *params)
103 {
104     return getResultBase(params);
105 }
106 
getResult(GLuint * params)107 gl::Error Query11::getResult(GLuint *params)
108 {
109     return getResultBase(params);
110 }
111 
getResult(GLint64 * params)112 gl::Error Query11::getResult(GLint64 *params)
113 {
114     return getResultBase(params);
115 }
116 
getResult(GLuint64 * params)117 gl::Error Query11::getResult(GLuint64 *params)
118 {
119     return getResultBase(params);
120 }
121 
isResultAvailable(bool * available)122 gl::Error Query11::isResultAvailable(bool *available)
123 {
124     ANGLE_TRY(flush(false));
125 
126     *available = mPendingQueries.empty();
127     return gl::NoError();
128 }
129 
pause()130 gl::Error Query11::pause()
131 {
132     if (mActiveQuery->query.valid())
133     {
134         ID3D11DeviceContext *context = mRenderer->getDeviceContext();
135         GLenum queryType             = getType();
136 
137         // If we are doing time elapsed query the end timestamp
138         if (queryType == GL_TIME_ELAPSED_EXT)
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(false);
150 }
151 
resume()152 gl::Error Query11::resume()
153 {
154     if (!mActiveQuery->query.valid())
155     {
156         ANGLE_TRY(flush(false));
157 
158         GLenum queryType         = getType();
159         D3D11_QUERY d3dQueryType = gl_d3d11::ConvertQueryType(queryType);
160 
161         D3D11_QUERY_DESC queryDesc;
162         queryDesc.Query     = d3dQueryType;
163         queryDesc.MiscFlags = 0;
164 
165         ANGLE_TRY(mRenderer->allocateResource(queryDesc, &mActiveQuery->query));
166 
167         // If we are doing time elapsed we also need a query to actually query the timestamp
168         if (queryType == GL_TIME_ELAPSED_EXT)
169         {
170             D3D11_QUERY_DESC desc;
171             desc.Query     = D3D11_QUERY_TIMESTAMP;
172             desc.MiscFlags = 0;
173 
174             ANGLE_TRY(mRenderer->allocateResource(desc, &mActiveQuery->beginTimestamp));
175             ANGLE_TRY(mRenderer->allocateResource(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 (queryType == GL_TIME_ELAPSED_EXT)
187         {
188             context->End(mActiveQuery->beginTimestamp.get());
189         }
190     }
191 
192     return gl::NoError();
193 }
194 
flush(bool force)195 gl::Error Query11::flush(bool force)
196 {
197     while (!mPendingQueries.empty())
198     {
199         QueryState *query = mPendingQueries.front().get();
200 
201         do
202         {
203             ANGLE_TRY(testQuery(query));
204             if (!query->finished && !force)
205             {
206                 return gl::NoError();
207             }
208         } while (!query->finished);
209 
210         mResultSum = MergeQueryResults(getType(), mResultSum, mResult);
211         mPendingQueries.pop_front();
212     }
213 
214     return gl::NoError();
215 }
216 
testQuery(QueryState * queryState)217 gl::Error Query11::testQuery(QueryState *queryState)
218 {
219     if (!queryState->finished)
220     {
221         ID3D11DeviceContext *context = mRenderer->getDeviceContext();
222         switch (getType())
223         {
224             case GL_ANY_SAMPLES_PASSED_EXT:
225             case GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT:
226             {
227                 ASSERT(queryState->query.valid());
228                 UINT64 numPixels = 0;
229                 HRESULT result =
230                     context->GetData(queryState->query.get(), &numPixels, sizeof(numPixels), 0);
231                 if (FAILED(result))
232                 {
233                     return gl::OutOfMemory()
234                            << "Failed to get the data of an internal query, " << gl::FmtHR(result);
235                 }
236 
237                 if (result == S_OK)
238                 {
239                     queryState->finished = true;
240                     mResult              = (numPixels > 0) ? GL_TRUE : GL_FALSE;
241                 }
242             }
243             break;
244 
245             case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
246             {
247                 ASSERT(queryState->query.valid());
248                 D3D11_QUERY_DATA_SO_STATISTICS soStats = {0};
249                 HRESULT result =
250                     context->GetData(queryState->query.get(), &soStats, sizeof(soStats), 0);
251                 if (FAILED(result))
252                 {
253                     return gl::OutOfMemory()
254                            << "Failed to get the data of an internal query, " << gl::FmtHR(result);
255                 }
256 
257                 if (result == S_OK)
258                 {
259                     queryState->finished = true;
260                     mResult              = static_cast<GLuint64>(soStats.NumPrimitivesWritten);
261                 }
262             }
263             break;
264 
265             case GL_TIME_ELAPSED_EXT:
266             {
267                 ASSERT(queryState->query.valid());
268                 ASSERT(queryState->beginTimestamp.valid());
269                 ASSERT(queryState->endTimestamp.valid());
270                 D3D11_QUERY_DATA_TIMESTAMP_DISJOINT timeStats = {0};
271                 HRESULT result =
272                     context->GetData(queryState->query.get(), &timeStats, sizeof(timeStats), 0);
273                 if (FAILED(result))
274                 {
275                     return gl::OutOfMemory()
276                            << "Failed to get the data of an internal query, " << gl::FmtHR(result);
277                 }
278 
279                 if (result == S_OK)
280                 {
281                     UINT64 beginTime = 0;
282                     HRESULT beginRes = context->GetData(queryState->beginTimestamp.get(),
283                                                         &beginTime, sizeof(UINT64), 0);
284                     if (FAILED(beginRes))
285                     {
286                         return gl::OutOfMemory() << "Failed to get the data of an internal query, "
287                                                  << gl::FmtHR(beginRes);
288                     }
289                     UINT64 endTime = 0;
290                     HRESULT endRes = context->GetData(queryState->endTimestamp.get(), &endTime,
291                                                       sizeof(UINT64), 0);
292                     if (FAILED(endRes))
293                     {
294                         return gl::OutOfMemory() << "Failed to get the data of an internal query, "
295                                                  << gl::FmtHR(endRes);
296                     }
297 
298                     if (beginRes == S_OK && endRes == S_OK)
299                     {
300                         queryState->finished = true;
301                         if (timeStats.Disjoint)
302                         {
303                             mRenderer->setGPUDisjoint();
304                         }
305                         static_assert(sizeof(UINT64) == sizeof(unsigned long long),
306                                       "D3D UINT64 isn't 64 bits");
307 
308                         angle::CheckedNumeric<UINT64> checkedTime(endTime);
309                         checkedTime -= beginTime;
310                         checkedTime *= 1000000000ull;
311                         checkedTime /= timeStats.Frequency;
312                         if (checkedTime.IsValid())
313                         {
314                             mResult = checkedTime.ValueOrDie();
315                         }
316                         else
317                         {
318                             mResult = std::numeric_limits<GLuint64>::max() / timeStats.Frequency;
319                             // If an overflow does somehow occur, there is no way the elapsed time
320                             // is accurate, so we generate a disjoint event
321                             mRenderer->setGPUDisjoint();
322                         }
323                     }
324                 }
325             }
326             break;
327 
328             case GL_TIMESTAMP_EXT:
329             {
330                 // D3D11 doesn't support GL timestamp queries as D3D timestamps are not guaranteed
331                 // to have any sort of continuity outside of a disjoint timestamp query block, which
332                 // GL depends on
333                 ASSERT(!queryState->query.valid());
334                 mResult              = 0;
335                 queryState->finished = true;
336             }
337             break;
338 
339             case GL_COMMANDS_COMPLETED_CHROMIUM:
340             {
341                 ASSERT(queryState->query.valid());
342                 BOOL completed = 0;
343                 HRESULT result =
344                     context->GetData(queryState->query.get(), &completed, sizeof(completed), 0);
345                 if (FAILED(result))
346                 {
347                     return gl::OutOfMemory()
348                            << "Failed to get the data of an internal query, " << gl::FmtHR(result);
349                 }
350 
351                 if (result == S_OK)
352                 {
353                     queryState->finished = true;
354                     ASSERT(completed == TRUE);
355                     mResult = (completed == TRUE) ? GL_TRUE : GL_FALSE;
356                 }
357             }
358             break;
359 
360             default:
361                 UNREACHABLE();
362                 break;
363         }
364 
365         if (!queryState->finished && mRenderer->testDeviceLost())
366         {
367             mRenderer->notifyDeviceLost();
368             return gl::OutOfMemory() << "Failed to test get query result, device is lost.";
369         }
370     }
371 
372     return gl::NoError();
373 }
374 
375 }  // namespace rx
376