blob: 269dcb703ecc76b8ff9d6b8f7a031738a31c8304 [file] [log] [blame]
Ian Ewell3ffd78b2016-01-22 16:09:42 -05001//
2// Copyright 2016 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// TimerQueriesTest.cpp
7// Various tests for EXT_disjoint_timer_query functionality and validation
8//
9
10#include "system_utils.h"
11#include "test_utils/ANGLETest.h"
12#include "random_utils.h"
13
14using namespace angle;
15
16class TimerQueriesTest : public ANGLETest
17{
18 protected:
19 TimerQueriesTest() : mProgram(0), mProgramCostly(0)
20 {
21 setWindowWidth(128);
22 setWindowHeight(128);
23 setConfigRedBits(8);
24 setConfigGreenBits(8);
25 setConfigBlueBits(8);
26 setConfigAlphaBits(8);
27 setConfigDepthBits(24);
28 }
29
30 virtual void SetUp()
31 {
32 ANGLETest::SetUp();
33
34 const std::string passthroughVS =
35 "attribute highp vec4 position; void main(void)\n"
36 "{\n"
37 " gl_Position = position;\n"
38 "}\n";
39
40 const std::string passthroughPS =
41 "precision highp float; void main(void)\n"
42 "{\n"
43 " gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
44 "}\n";
45
46 const std::string costlyVS =
47 "attribute highp vec4 position; varying highp vec4 testPos; void main(void)\n"
48 "{\n"
49 " testPos = position;\n"
50 " gl_Position = position;\n"
51 "}\n";
52
53 const std::string costlyPS =
54 "precision highp float; varying highp vec4 testPos; void main(void)\n"
55 "{\n"
56 " vec4 test = testPos;\n"
57 " for (int i = 0; i < 500; i++)\n"
58 " {\n"
59 " test = sqrt(test);\n"
60 " }\n"
61 " gl_FragColor = test;\n"
62 "}\n";
63
64 mProgram = CompileProgram(passthroughVS, passthroughPS);
65 ASSERT_NE(0u, mProgram) << "shader compilation failed.";
66
67 mProgramCostly = CompileProgram(costlyVS, costlyPS);
68 ASSERT_NE(0u, mProgramCostly) << "shader compilation failed.";
69 }
70
71 virtual void TearDown()
72 {
73 glDeleteProgram(mProgram);
74 glDeleteProgram(mProgramCostly);
75 ANGLETest::TearDown();
76 }
77
78 GLuint mProgram;
79 GLuint mProgramCostly;
80};
81
82// Tests the time elapsed query
83TEST_P(TimerQueriesTest, TimeElapsed)
84{
85 if (!extensionEnabled("GL_EXT_disjoint_timer_query"))
86 {
87 std::cout << "Test skipped because GL_EXT_disjoint_timer_query is not available."
88 << std::endl;
89 return;
90 }
91
92 GLint queryTimeElapsedBits = 0;
93 glGetQueryivEXT(GL_TIME_ELAPSED_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimeElapsedBits);
94 ASSERT_GL_NO_ERROR();
95
96 std::cout << "Time elapsed counter bits: " << queryTimeElapsedBits << std::endl;
97
98 // Skip test if the number of bits is 0
99 if (queryTimeElapsedBits == 0)
100 {
101 std::cout << "Test skipped because of 0 counter bits" << std::endl;
102 return;
103 }
104
105 glDepthMask(GL_TRUE);
106 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
107
108 GLuint query1 = 0;
109 GLuint query2 = 0;
110 glGenQueriesEXT(1, &query1);
111 glGenQueriesEXT(1, &query2);
112
113 // Test time elapsed for a single quad
114 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query1);
115 drawQuad(mProgram, "position", 0.8f);
116 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
117 ASSERT_GL_NO_ERROR();
118
119 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
120
121 // Test time elapsed for costly quad
122 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query2);
123 drawQuad(mProgramCostly, "position", 0.8f);
124 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
125 ASSERT_GL_NO_ERROR();
126
127 swapBuffers();
128
Ian Ewell536ebf42016-02-09 13:20:12 -0500129 int timeout = 200000;
Ian Ewell3ffd78b2016-01-22 16:09:42 -0500130 GLuint ready = GL_FALSE;
131 while (ready == GL_FALSE && timeout > 0)
132 {
133 angle::Sleep(0);
134 glGetQueryObjectuivEXT(query1, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
135 timeout--;
136 }
137 ready = GL_FALSE;
138 while (ready == GL_FALSE && timeout > 0)
139 {
140 angle::Sleep(0);
141 glGetQueryObjectuivEXT(query2, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
142 timeout--;
143 }
144 ASSERT_LT(0, timeout) << "Query result available timed out" << std::endl;
145
146 GLuint64 result1 = 0;
147 GLuint64 result2 = 0;
148 glGetQueryObjectui64vEXT(query1, GL_QUERY_RESULT_EXT, &result1);
149 glGetQueryObjectui64vEXT(query2, GL_QUERY_RESULT_EXT, &result2);
150 ASSERT_GL_NO_ERROR();
151
152 glDeleteQueriesEXT(1, &query1);
153 glDeleteQueriesEXT(1, &query2);
154 ASSERT_GL_NO_ERROR();
155
156 std::cout << "Elapsed time: " << result1 << " cheap quad" << std::endl;
157 std::cout << "Elapsed time: " << result2 << " costly quad" << std::endl;
158
159 // The time elapsed should be nonzero
160 EXPECT_LT(0ul, result1);
161 EXPECT_LT(0ul, result2);
162
163 // The costly quad should take longer than the cheap quad
164 EXPECT_LT(result1, result2);
165}
166
Ian Ewell292f0052016-02-04 10:37:32 -0500167// Tests time elapsed for a non draw call (texture upload)
168TEST_P(TimerQueriesTest, TimeElapsedTextureTest)
169{
170 // OSX drivers don't seem to properly time non-draw calls so we skip the test on Mac
Jamie Madill7208f692016-02-29 10:47:35 -0500171 if (IsOSX())
Ian Ewell292f0052016-02-04 10:37:32 -0500172 {
173 std::cout << "Test skipped on OSX" << std::endl;
174 return;
175 }
176
177 if (!extensionEnabled("GL_EXT_disjoint_timer_query"))
178 {
179 std::cout << "Test skipped because GL_EXT_disjoint_timer_query is not available."
180 << std::endl;
181 return;
182 }
183
184 GLint queryTimeElapsedBits = 0;
185 glGetQueryivEXT(GL_TIME_ELAPSED_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimeElapsedBits);
186 ASSERT_GL_NO_ERROR();
187
188 std::cout << "Time elapsed counter bits: " << queryTimeElapsedBits << std::endl;
189
190 // Skip test if the number of bits is 0
191 if (queryTimeElapsedBits == 0)
192 {
193 std::cout << "Test skipped because of 0 counter bits" << std::endl;
194 return;
195 }
196
197 GLubyte pixels[] = {0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0};
198
199 // Query and texture initialization
200 GLuint texture;
201 GLuint query = 0;
202 glGenQueriesEXT(1, &query);
203 glGenTextures(1, &texture);
204
205 // Upload a texture inside the query
206 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query);
207 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
208 glBindTexture(GL_TEXTURE_2D, texture);
209 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
210 glGenerateMipmap(GL_TEXTURE_2D);
211 glFinish();
212 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
213 ASSERT_GL_NO_ERROR();
214
215 int timeout = 200000;
216 GLuint ready = GL_FALSE;
217 while (ready == GL_FALSE && timeout > 0)
218 {
219 angle::Sleep(0);
220 glGetQueryObjectuivEXT(query, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
221 timeout--;
222 }
223 ASSERT_LT(0, timeout) << "Query result available timed out" << std::endl;
224
225 GLuint64 result = 0;
226 glGetQueryObjectui64vEXT(query, GL_QUERY_RESULT_EXT, &result);
227 ASSERT_GL_NO_ERROR();
228
229 glDeleteTextures(1, &texture);
230 glDeleteQueriesEXT(1, &query);
231
232 std::cout << "Elapsed time: " << result << std::endl;
233 EXPECT_LT(0ul, result);
234}
235
Ian Ewell3ffd78b2016-01-22 16:09:42 -0500236// Tests validation of query functions with respect to elapsed time query
237TEST_P(TimerQueriesTest, TimeElapsedValidationTest)
238{
239 if (!extensionEnabled("GL_EXT_disjoint_timer_query"))
240 {
241 std::cout << "Test skipped because GL_EXT_disjoint_timer_query is not available."
242 << std::endl;
243 return;
244 }
245
246 GLint queryTimeElapsedBits = 0;
247 glGetQueryivEXT(GL_TIME_ELAPSED_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimeElapsedBits);
248 ASSERT_GL_NO_ERROR();
249
250 std::cout << "Time elapsed counter bits: " << queryTimeElapsedBits << std::endl;
251
252 // Skip test if the number of bits is 0
253 if (queryTimeElapsedBits == 0)
254 {
255 std::cout << "Test skipped because of 0 counter bits" << std::endl;
256 return;
257 }
258
259 GLuint query = 0;
260 glGenQueriesEXT(-1, &query);
261 EXPECT_GL_ERROR(GL_INVALID_VALUE);
262
263 glGenQueriesEXT(1, &query);
264 EXPECT_GL_NO_ERROR();
265
266 glBeginQueryEXT(GL_TIMESTAMP_EXT, query);
267 EXPECT_GL_ERROR(GL_INVALID_ENUM);
268
269 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, 0);
270 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
271
272 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
273 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
274
275 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query);
276 EXPECT_GL_NO_ERROR();
277
278 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query);
279 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
280
281 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
282 EXPECT_GL_NO_ERROR();
283
284 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
285 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
286}
287
Ian Ewell292f0052016-02-04 10:37:32 -0500288// Tests timer queries operating under multiple EGL contexts with mid-query switching
289TEST_P(TimerQueriesTest, TimeElapsedMulticontextTest)
290{
291 if (!extensionEnabled("GL_EXT_disjoint_timer_query"))
292 {
293 std::cout << "Test skipped because GL_EXT_disjoint_timer_query is not available."
294 << std::endl;
295 return;
296 }
297
298 GLint queryTimeElapsedBits = 0;
299 glGetQueryivEXT(GL_TIME_ELAPSED_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimeElapsedBits);
300 ASSERT_GL_NO_ERROR();
301
302 std::cout << "Time elapsed counter bits: " << queryTimeElapsedBits << std::endl;
303
304 // Skip test if the number of bits is 0
305 if (queryTimeElapsedBits == 0)
306 {
307 std::cout << "Test skipped because of 0 counter bits" << std::endl;
308 return;
309 }
310
Ian Ewell89f28452016-02-10 11:33:07 -0500311 // Without a glClear, the first draw call on GL takes a huge amount of time when run after the
312 // D3D test on certain NVIDIA drivers
313 glDepthMask(GL_TRUE);
314 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
Ian Ewell292f0052016-02-04 10:37:32 -0500315
316 EGLint contextAttributes[] = {
317 EGL_CONTEXT_MAJOR_VERSION_KHR,
318 GetParam().majorVersion,
319 EGL_CONTEXT_MINOR_VERSION_KHR,
320 GetParam().minorVersion,
321 EGL_NONE,
322 };
323
324 EGLWindow *window = getEGLWindow();
325
326 EGLDisplay display = window->getDisplay();
327 EGLConfig config = window->getConfig();
328 EGLSurface surface = window->getSurface();
329
330 struct ContextInfo
331 {
332 EGLContext context;
333 GLuint program;
334 GLuint query;
335 EGLDisplay display;
336
337 ContextInfo() : context(EGL_NO_CONTEXT), program(0), query(0), display(EGL_NO_DISPLAY) {}
338
339 ~ContextInfo()
340 {
341 if (context != EGL_NO_CONTEXT && display != EGL_NO_DISPLAY)
342 {
343 eglDestroyContext(display, context);
344 }
345 }
346 };
347 ContextInfo contexts[2];
348
349 // Shaders
350 const std::string cheapVS =
351 "attribute highp vec4 position; void main(void)\n"
352 "{\n"
353 " gl_Position = position;\n"
354 "}\n";
355
356 const std::string cheapPS =
357 "precision highp float; void main(void)\n"
358 "{\n"
359 " gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
360 "}\n";
361
362 const std::string costlyVS =
363 "attribute highp vec4 position; varying highp vec4 testPos; void main(void)\n"
364 "{\n"
365 " testPos = position;\n"
366 " gl_Position = position;\n"
367 "}\n";
368
369 const std::string costlyPS =
370 "precision highp float; varying highp vec4 testPos; void main(void)\n"
371 "{\n"
372 " vec4 test = testPos;\n"
373 " for (int i = 0; i < 500; i++)\n"
374 " {\n"
375 " test = sqrt(test);\n"
376 " }\n"
377 " gl_FragColor = test;\n"
378 "}\n";
379
380 // Setup the first context with a cheap shader
381 contexts[0].context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes);
382 contexts[0].display = display;
383 ASSERT_NE(contexts[0].context, EGL_NO_CONTEXT);
384 eglMakeCurrent(display, surface, surface, contexts[0].context);
385 contexts[0].program = CompileProgram(cheapVS, cheapPS);
386 glGenQueriesEXT(1, &contexts[0].query);
387 ASSERT_GL_NO_ERROR();
388
389 // Setup the second context with an expensive shader
390 contexts[1].context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes);
391 contexts[1].display = display;
392 ASSERT_NE(contexts[1].context, EGL_NO_CONTEXT);
393 eglMakeCurrent(display, surface, surface, contexts[1].context);
394 contexts[1].program = CompileProgram(costlyVS, costlyPS);
395 glGenQueriesEXT(1, &contexts[1].query);
396 ASSERT_GL_NO_ERROR();
397
398 // Start the query and draw a quad on the first context without ending the query
399 eglMakeCurrent(display, surface, surface, contexts[0].context);
400 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, contexts[0].query);
401 drawQuad(contexts[0].program, "position", 0.8f);
402 ASSERT_GL_NO_ERROR();
403
404 // Switch contexts, draw the expensive quad and end its query
405 eglMakeCurrent(display, surface, surface, contexts[1].context);
406 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, contexts[1].query);
407 drawQuad(contexts[1].program, "position", 0.8f);
408 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
409 ASSERT_GL_NO_ERROR();
410
411 // Go back to the first context, end its query, and get the result
412 eglMakeCurrent(display, surface, surface, contexts[0].context);
413 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
Ian Ewell292f0052016-02-04 10:37:32 -0500414
415 GLuint64 result1 = 0;
416 GLuint64 result2 = 0;
417 glGetQueryObjectui64vEXT(contexts[0].query, GL_QUERY_RESULT_EXT, &result1);
418 glDeleteQueriesEXT(1, &contexts[0].query);
419 glDeleteProgram(contexts[0].program);
420 ASSERT_GL_NO_ERROR();
421
Ian Ewell89f28452016-02-10 11:33:07 -0500422 // Get the 2nd context's results
Ian Ewell292f0052016-02-04 10:37:32 -0500423 eglMakeCurrent(display, surface, surface, contexts[1].context);
Ian Ewell292f0052016-02-04 10:37:32 -0500424 glGetQueryObjectui64vEXT(contexts[1].query, GL_QUERY_RESULT_EXT, &result2);
425 glDeleteQueriesEXT(1, &contexts[1].query);
426 glDeleteProgram(contexts[1].program);
427 ASSERT_GL_NO_ERROR();
428
429 // Switch back to main context
430 eglMakeCurrent(display, surface, surface, window->getContext());
431
432 // Compare the results. The cheap quad should be smaller than the expensive one if
433 // virtualization is working correctly
434 std::cout << "Elapsed time: " << result1 << " cheap quad" << std::endl;
435 std::cout << "Elapsed time: " << result2 << " costly quad" << std::endl;
436 EXPECT_LT(0ul, result1);
437 EXPECT_LT(0ul, result2);
438 EXPECT_LT(result1, result2);
439}
440
Ian Ewell3ffd78b2016-01-22 16:09:42 -0500441// Tests GPU timestamp functionality
442TEST_P(TimerQueriesTest, Timestamp)
443{
444 if (!extensionEnabled("GL_EXT_disjoint_timer_query"))
445 {
446 std::cout << "Test skipped because GL_EXT_disjoint_timer_query is not available."
447 << std::endl;
448 return;
449 }
450
451 GLint queryTimestampBits = 0;
452 glGetQueryivEXT(GL_TIMESTAMP_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimestampBits);
453 ASSERT_GL_NO_ERROR();
454
455 std::cout << "Timestamp counter bits: " << queryTimestampBits << std::endl;
456
457 // Macs for some reason return 0 bits so skip the test for now if either are 0
458 if (queryTimestampBits == 0)
459 {
460 std::cout << "Test skipped because of 0 counter bits" << std::endl;
461 return;
462 }
463
464 glDepthMask(GL_TRUE);
465 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
466
467 GLuint query1 = 0;
468 GLuint query2 = 0;
469 glGenQueriesEXT(1, &query1);
470 glGenQueriesEXT(1, &query2);
471 glQueryCounterEXT(query1, GL_TIMESTAMP_EXT);
472 drawQuad(mProgram, "position", 0.8f);
473 glQueryCounterEXT(query2, GL_TIMESTAMP_EXT);
474
475 ASSERT_GL_NO_ERROR();
476
477 swapBuffers();
478
Ian Ewell536ebf42016-02-09 13:20:12 -0500479 int timeout = 200000;
Ian Ewell3ffd78b2016-01-22 16:09:42 -0500480 GLuint ready = GL_FALSE;
481 while (ready == GL_FALSE && timeout > 0)
482 {
483 angle::Sleep(0);
484 glGetQueryObjectuivEXT(query1, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
485 timeout--;
486 }
487 ready = GL_FALSE;
488 while (ready == GL_FALSE && timeout > 0)
489 {
490 angle::Sleep(0);
491 glGetQueryObjectuivEXT(query2, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
492 timeout--;
493 }
494 ASSERT_LT(0, timeout) << "Query result available timed out" << std::endl;
495
496 GLuint64 result1 = 0;
497 GLuint64 result2 = 0;
498 glGetQueryObjectui64vEXT(query1, GL_QUERY_RESULT_EXT, &result1);
499 glGetQueryObjectui64vEXT(query2, GL_QUERY_RESULT_EXT, &result2);
500
501 ASSERT_GL_NO_ERROR();
502
503 glDeleteQueriesEXT(1, &query1);
504 glDeleteQueriesEXT(1, &query2);
505
506 std::cout << "Timestamps: " << result1 << " " << result2 << std::endl;
507 EXPECT_LT(0ul, result1);
508 EXPECT_LT(0ul, result2);
509 EXPECT_LT(result1, result2);
510}
511
Ian Ewell53f59f42016-01-28 17:36:55 -0500512class TimerQueriesTestES3 : public TimerQueriesTest
513{
Ian Ewell53f59f42016-01-28 17:36:55 -0500514};
515
516// Tests getting timestamps via glGetInteger64v
517TEST_P(TimerQueriesTestES3, TimestampGetInteger64)
518{
519 if (!extensionEnabled("GL_EXT_disjoint_timer_query"))
520 {
521 std::cout << "Test skipped because GL_EXT_disjoint_timer_query is not available."
522 << std::endl;
523 return;
524 }
525
526 GLint queryTimestampBits = 0;
527 glGetQueryivEXT(GL_TIMESTAMP_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimestampBits);
528 ASSERT_GL_NO_ERROR();
529
530 std::cout << "Timestamp counter bits: " << queryTimestampBits << std::endl;
531
532 if (queryTimestampBits == 0)
533 {
534 std::cout << "Test skipped because of 0 counter bits" << std::endl;
535 return;
536 }
537
538 glDepthMask(GL_TRUE);
539 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
540
541 GLint64 result1 = 0;
542 GLint64 result2 = 0;
543 glGetInteger64v(GL_TIMESTAMP_EXT, &result1);
544 drawQuad(mProgram, "position", 0.8f);
545 glGetInteger64v(GL_TIMESTAMP_EXT, &result2);
546 ASSERT_GL_NO_ERROR();
547 std::cout << "Timestamps (getInteger64v): " << result1 << " " << result2 << std::endl;
548 EXPECT_LT(0l, result1);
549 EXPECT_LT(0l, result2);
550 EXPECT_LT(result1, result2);
551}
552
Ian Ewell3ffd78b2016-01-22 16:09:42 -0500553ANGLE_INSTANTIATE_TEST(TimerQueriesTest,
554 ES2_D3D9(),
555 ES2_D3D11(),
556 ES3_D3D11(),
557 ES2_OPENGL(),
558 ES3_OPENGL());
Ian Ewell53f59f42016-01-28 17:36:55 -0500559
Ian Ewellf04f6672016-02-03 10:50:16 -0500560ANGLE_INSTANTIATE_TEST(TimerQueriesTestES3, ES3_D3D11(), ES3_OPENGL());