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