blob: 70d9cfd0d24fe8f68abe886b346e5158bb94c5b5 [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
171 if (isOSX())
172 {
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
311 // D3D multicontext isn't implemented yet
312 if (GetParam() == ES3_D3D11() || GetParam() == ES2_D3D11())
313 {
314 std::cout
315 << "Test skipped because the D3D backends cannot support simultaneous timer queries yet"
316 << std::endl;
317 return;
318 }
319
320 EGLint contextAttributes[] = {
321 EGL_CONTEXT_MAJOR_VERSION_KHR,
322 GetParam().majorVersion,
323 EGL_CONTEXT_MINOR_VERSION_KHR,
324 GetParam().minorVersion,
325 EGL_NONE,
326 };
327
328 EGLWindow *window = getEGLWindow();
329
330 EGLDisplay display = window->getDisplay();
331 EGLConfig config = window->getConfig();
332 EGLSurface surface = window->getSurface();
333
334 struct ContextInfo
335 {
336 EGLContext context;
337 GLuint program;
338 GLuint query;
339 EGLDisplay display;
340
341 ContextInfo() : context(EGL_NO_CONTEXT), program(0), query(0), display(EGL_NO_DISPLAY) {}
342
343 ~ContextInfo()
344 {
345 if (context != EGL_NO_CONTEXT && display != EGL_NO_DISPLAY)
346 {
347 eglDestroyContext(display, context);
348 }
349 }
350 };
351 ContextInfo contexts[2];
352
353 // Shaders
354 const std::string cheapVS =
355 "attribute highp vec4 position; void main(void)\n"
356 "{\n"
357 " gl_Position = position;\n"
358 "}\n";
359
360 const std::string cheapPS =
361 "precision highp float; void main(void)\n"
362 "{\n"
363 " gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
364 "}\n";
365
366 const std::string costlyVS =
367 "attribute highp vec4 position; varying highp vec4 testPos; void main(void)\n"
368 "{\n"
369 " testPos = position;\n"
370 " gl_Position = position;\n"
371 "}\n";
372
373 const std::string costlyPS =
374 "precision highp float; varying highp vec4 testPos; void main(void)\n"
375 "{\n"
376 " vec4 test = testPos;\n"
377 " for (int i = 0; i < 500; i++)\n"
378 " {\n"
379 " test = sqrt(test);\n"
380 " }\n"
381 " gl_FragColor = test;\n"
382 "}\n";
383
384 // Setup the first context with a cheap shader
385 contexts[0].context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes);
386 contexts[0].display = display;
387 ASSERT_NE(contexts[0].context, EGL_NO_CONTEXT);
388 eglMakeCurrent(display, surface, surface, contexts[0].context);
389 contexts[0].program = CompileProgram(cheapVS, cheapPS);
390 glGenQueriesEXT(1, &contexts[0].query);
391 ASSERT_GL_NO_ERROR();
392
393 // Setup the second context with an expensive shader
394 contexts[1].context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes);
395 contexts[1].display = display;
396 ASSERT_NE(contexts[1].context, EGL_NO_CONTEXT);
397 eglMakeCurrent(display, surface, surface, contexts[1].context);
398 contexts[1].program = CompileProgram(costlyVS, costlyPS);
399 glGenQueriesEXT(1, &contexts[1].query);
400 ASSERT_GL_NO_ERROR();
401
402 // Start the query and draw a quad on the first context without ending the query
403 eglMakeCurrent(display, surface, surface, contexts[0].context);
404 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, contexts[0].query);
405 drawQuad(contexts[0].program, "position", 0.8f);
406 ASSERT_GL_NO_ERROR();
407
408 // Switch contexts, draw the expensive quad and end its query
409 eglMakeCurrent(display, surface, surface, contexts[1].context);
410 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, contexts[1].query);
411 drawQuad(contexts[1].program, "position", 0.8f);
412 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
413 ASSERT_GL_NO_ERROR();
414
415 // Go back to the first context, end its query, and get the result
416 eglMakeCurrent(display, surface, surface, contexts[0].context);
417 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
418 int timeout = 20000;
419 GLuint ready = GL_FALSE;
420 while (ready == GL_FALSE && timeout > 0)
421 {
422 angle::Sleep(0);
423 glGetQueryObjectuivEXT(contexts[0].query, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
424 timeout--;
425 }
426 ASSERT_LT(0, timeout) << "Query result available timed out" << std::endl;
427
428 GLuint64 result1 = 0;
429 GLuint64 result2 = 0;
430 glGetQueryObjectui64vEXT(contexts[0].query, GL_QUERY_RESULT_EXT, &result1);
431 glDeleteQueriesEXT(1, &contexts[0].query);
432 glDeleteProgram(contexts[0].program);
433 ASSERT_GL_NO_ERROR();
434
435 // Get the 2nd contexts results
436 eglMakeCurrent(display, surface, surface, contexts[1].context);
437 timeout = 20000;
438 ready = GL_FALSE;
439 while (ready == GL_FALSE && timeout > 0)
440 {
441 angle::Sleep(0);
442 glGetQueryObjectuivEXT(contexts[1].query, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
443 timeout--;
444 }
445 ASSERT_LT(0, timeout) << "Query result available timed out" << std::endl;
446 glGetQueryObjectui64vEXT(contexts[1].query, GL_QUERY_RESULT_EXT, &result2);
447 glDeleteQueriesEXT(1, &contexts[1].query);
448 glDeleteProgram(contexts[1].program);
449 ASSERT_GL_NO_ERROR();
450
451 // Switch back to main context
452 eglMakeCurrent(display, surface, surface, window->getContext());
453
454 // Compare the results. The cheap quad should be smaller than the expensive one if
455 // virtualization is working correctly
456 std::cout << "Elapsed time: " << result1 << " cheap quad" << std::endl;
457 std::cout << "Elapsed time: " << result2 << " costly quad" << std::endl;
458 EXPECT_LT(0ul, result1);
459 EXPECT_LT(0ul, result2);
460 EXPECT_LT(result1, result2);
461}
462
Ian Ewell3ffd78b2016-01-22 16:09:42 -0500463// Tests GPU timestamp functionality
464TEST_P(TimerQueriesTest, Timestamp)
465{
466 if (!extensionEnabled("GL_EXT_disjoint_timer_query"))
467 {
468 std::cout << "Test skipped because GL_EXT_disjoint_timer_query is not available."
469 << std::endl;
470 return;
471 }
472
473 GLint queryTimestampBits = 0;
474 glGetQueryivEXT(GL_TIMESTAMP_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimestampBits);
475 ASSERT_GL_NO_ERROR();
476
477 std::cout << "Timestamp counter bits: " << queryTimestampBits << std::endl;
478
479 // Macs for some reason return 0 bits so skip the test for now if either are 0
480 if (queryTimestampBits == 0)
481 {
482 std::cout << "Test skipped because of 0 counter bits" << std::endl;
483 return;
484 }
485
486 glDepthMask(GL_TRUE);
487 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
488
489 GLuint query1 = 0;
490 GLuint query2 = 0;
491 glGenQueriesEXT(1, &query1);
492 glGenQueriesEXT(1, &query2);
493 glQueryCounterEXT(query1, GL_TIMESTAMP_EXT);
494 drawQuad(mProgram, "position", 0.8f);
495 glQueryCounterEXT(query2, GL_TIMESTAMP_EXT);
496
497 ASSERT_GL_NO_ERROR();
498
499 swapBuffers();
500
Ian Ewell536ebf42016-02-09 13:20:12 -0500501 int timeout = 200000;
Ian Ewell3ffd78b2016-01-22 16:09:42 -0500502 GLuint ready = GL_FALSE;
503 while (ready == GL_FALSE && timeout > 0)
504 {
505 angle::Sleep(0);
506 glGetQueryObjectuivEXT(query1, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
507 timeout--;
508 }
509 ready = GL_FALSE;
510 while (ready == GL_FALSE && timeout > 0)
511 {
512 angle::Sleep(0);
513 glGetQueryObjectuivEXT(query2, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
514 timeout--;
515 }
516 ASSERT_LT(0, timeout) << "Query result available timed out" << std::endl;
517
518 GLuint64 result1 = 0;
519 GLuint64 result2 = 0;
520 glGetQueryObjectui64vEXT(query1, GL_QUERY_RESULT_EXT, &result1);
521 glGetQueryObjectui64vEXT(query2, GL_QUERY_RESULT_EXT, &result2);
522
523 ASSERT_GL_NO_ERROR();
524
525 glDeleteQueriesEXT(1, &query1);
526 glDeleteQueriesEXT(1, &query2);
527
528 std::cout << "Timestamps: " << result1 << " " << result2 << std::endl;
529 EXPECT_LT(0ul, result1);
530 EXPECT_LT(0ul, result2);
531 EXPECT_LT(result1, result2);
532}
533
Ian Ewell53f59f42016-01-28 17:36:55 -0500534class TimerQueriesTestES3 : public TimerQueriesTest
535{
Ian Ewell53f59f42016-01-28 17:36:55 -0500536};
537
538// Tests getting timestamps via glGetInteger64v
539TEST_P(TimerQueriesTestES3, TimestampGetInteger64)
540{
541 if (!extensionEnabled("GL_EXT_disjoint_timer_query"))
542 {
543 std::cout << "Test skipped because GL_EXT_disjoint_timer_query is not available."
544 << std::endl;
545 return;
546 }
547
548 GLint queryTimestampBits = 0;
549 glGetQueryivEXT(GL_TIMESTAMP_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimestampBits);
550 ASSERT_GL_NO_ERROR();
551
552 std::cout << "Timestamp counter bits: " << queryTimestampBits << std::endl;
553
554 if (queryTimestampBits == 0)
555 {
556 std::cout << "Test skipped because of 0 counter bits" << std::endl;
557 return;
558 }
559
560 glDepthMask(GL_TRUE);
561 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
562
563 GLint64 result1 = 0;
564 GLint64 result2 = 0;
565 glGetInteger64v(GL_TIMESTAMP_EXT, &result1);
566 drawQuad(mProgram, "position", 0.8f);
567 glGetInteger64v(GL_TIMESTAMP_EXT, &result2);
568 ASSERT_GL_NO_ERROR();
569 std::cout << "Timestamps (getInteger64v): " << result1 << " " << result2 << std::endl;
570 EXPECT_LT(0l, result1);
571 EXPECT_LT(0l, result2);
572 EXPECT_LT(result1, result2);
573}
574
Ian Ewell3ffd78b2016-01-22 16:09:42 -0500575ANGLE_INSTANTIATE_TEST(TimerQueriesTest,
576 ES2_D3D9(),
577 ES2_D3D11(),
578 ES3_D3D11(),
579 ES2_OPENGL(),
580 ES3_OPENGL());
Ian Ewell53f59f42016-01-28 17:36:55 -0500581
Ian Ewellf04f6672016-02-03 10:50:16 -0500582ANGLE_INSTANTIATE_TEST(TimerQueriesTestES3, ES3_D3D11(), ES3_OPENGL());