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