1 //
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 "test_utils/ANGLETest.h"
11 #include "util/EGLWindow.h"
12 #include "util/random_utils.h"
13 #include "util/test_utils.h"
14
15 using namespace angle;
16
17 class TimerQueriesTest : public ANGLETest
18 {
19 protected:
TimerQueriesTest()20 TimerQueriesTest() : mProgram(0), mProgramCostly(0)
21 {
22 setWindowWidth(128);
23 setWindowHeight(128);
24 setConfigRedBits(8);
25 setConfigGreenBits(8);
26 setConfigBlueBits(8);
27 setConfigAlphaBits(8);
28 setConfigDepthBits(24);
29 }
30
testSetUp()31 void testSetUp() override
32 {
33 constexpr char kCostlyVS[] =
34 "attribute highp vec4 position; varying highp vec4 testPos; void main(void)\n"
35 "{\n"
36 " testPos = position;\n"
37 " gl_Position = position;\n"
38 "}\n";
39
40 constexpr char kCostlyFS[] =
41 "precision highp float; varying highp vec4 testPos; void main(void)\n"
42 "{\n"
43 " vec4 test = testPos;\n"
44 " for (int i = 0; i < 500; i++)\n"
45 " {\n"
46 " test = sqrt(test);\n"
47 " }\n"
48 " gl_FragColor = test;\n"
49 "}\n";
50
51 mProgram = CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
52 ASSERT_NE(0u, mProgram) << "shader compilation failed.";
53
54 mProgramCostly = CompileProgram(kCostlyVS, kCostlyFS);
55 ASSERT_NE(0u, mProgramCostly) << "shader compilation failed.";
56 }
57
testTearDown()58 void testTearDown() override
59 {
60 glDeleteProgram(mProgram);
61 glDeleteProgram(mProgramCostly);
62 }
63
64 GLuint mProgram;
65 GLuint mProgramCostly;
66 };
67
68 // Test that all proc addresses are loadable
TEST_P(TimerQueriesTest,ProcAddresses)69 TEST_P(TimerQueriesTest, ProcAddresses)
70 {
71 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_disjoint_timer_query"));
72
73 ASSERT_NE(nullptr, eglGetProcAddress("glGenQueriesEXT"));
74 ASSERT_NE(nullptr, eglGetProcAddress("glDeleteQueriesEXT"));
75 ASSERT_NE(nullptr, eglGetProcAddress("glIsQueryEXT"));
76 ASSERT_NE(nullptr, eglGetProcAddress("glBeginQueryEXT"));
77 ASSERT_NE(nullptr, eglGetProcAddress("glEndQueryEXT"));
78 ASSERT_NE(nullptr, eglGetProcAddress("glQueryCounterEXT"));
79 ASSERT_NE(nullptr, eglGetProcAddress("glGetQueryivEXT"));
80 ASSERT_NE(nullptr, eglGetProcAddress("glGetQueryObjectivEXT"));
81 ASSERT_NE(nullptr, eglGetProcAddress("glGetQueryObjectuivEXT"));
82 ASSERT_NE(nullptr, eglGetProcAddress("glGetQueryObjecti64vEXT"));
83 ASSERT_NE(nullptr, eglGetProcAddress("glGetQueryObjectui64vEXT"));
84 }
85
86 // Tests the time elapsed query
TEST_P(TimerQueriesTest,TimeElapsed)87 TEST_P(TimerQueriesTest, TimeElapsed)
88 {
89 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_disjoint_timer_query"));
90
91 GLint queryTimeElapsedBits = 0;
92 glGetQueryivEXT(GL_TIME_ELAPSED_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimeElapsedBits);
93 ASSERT_GL_NO_ERROR();
94
95 std::cout << "Time elapsed counter bits: " << queryTimeElapsedBits << std::endl;
96
97 // Skip test if the number of bits is 0
98 ANGLE_SKIP_TEST_IF(!queryTimeElapsedBits);
99
100 glDepthMask(GL_TRUE);
101 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
102
103 GLuint query1 = 0;
104 GLuint query2 = 0;
105 glGenQueriesEXT(1, &query1);
106 glGenQueriesEXT(1, &query2);
107
108 // Test time elapsed for a single quad
109 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query1);
110 drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.8f);
111 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
112 ASSERT_GL_NO_ERROR();
113
114 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
115
116 // Test time elapsed for costly quad
117 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query2);
118 drawQuad(mProgramCostly, "position", 0.8f);
119 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
120 ASSERT_GL_NO_ERROR();
121
122 swapBuffers();
123
124 int timeout = 200000;
125 GLuint ready = GL_FALSE;
126 while (ready == GL_FALSE && timeout > 0)
127 {
128 angle::Sleep(0);
129 glGetQueryObjectuivEXT(query1, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
130 timeout--;
131 }
132 ready = GL_FALSE;
133 while (ready == GL_FALSE && timeout > 0)
134 {
135 angle::Sleep(0);
136 glGetQueryObjectuivEXT(query2, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
137 timeout--;
138 }
139 ASSERT_LT(0, timeout) << "Query result available timed out" << std::endl;
140
141 GLuint64 result1 = 0;
142 GLuint64 result2 = 0;
143 glGetQueryObjectui64vEXT(query1, GL_QUERY_RESULT_EXT, &result1);
144 glGetQueryObjectui64vEXT(query2, GL_QUERY_RESULT_EXT, &result2);
145 ASSERT_GL_NO_ERROR();
146
147 glDeleteQueriesEXT(1, &query1);
148 glDeleteQueriesEXT(1, &query2);
149 ASSERT_GL_NO_ERROR();
150
151 std::cout << "Elapsed time: " << result1 << " cheap quad" << std::endl;
152 std::cout << "Elapsed time: " << result2 << " costly quad" << std::endl;
153
154 // The time elapsed should be nonzero
155 EXPECT_LT(0ul, result1);
156 EXPECT_LT(0ul, result2);
157
158 // The time elapsed should be less than a second. Not an actual
159 // requirement, but longer than a second to draw something basic hints at
160 // an issue with the queries themselves.
161 EXPECT_LT(result1, 1000000000ul);
162 EXPECT_LT(result2, 1000000000ul);
163
164 // TODO(geofflang): Re-enable this check when it is non-flaky
165 // The costly quad should take longer than the cheap quad
166 // EXPECT_LT(result1, result2);
167 }
168
169 // Tests time elapsed for a non draw call (texture upload)
TEST_P(TimerQueriesTest,TimeElapsedTextureTest)170 TEST_P(TimerQueriesTest, TimeElapsedTextureTest)
171 {
172 // OSX drivers don't seem to properly time non-draw calls so we skip the test on Mac
173 ANGLE_SKIP_TEST_IF(IsOSX());
174
175 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_disjoint_timer_query"));
176
177 GLint queryTimeElapsedBits = 0;
178 glGetQueryivEXT(GL_TIME_ELAPSED_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimeElapsedBits);
179 ASSERT_GL_NO_ERROR();
180
181 std::cout << "Time elapsed counter bits: " << queryTimeElapsedBits << std::endl;
182
183 // Skip test if the number of bits is 0
184 ANGLE_SKIP_TEST_IF(!queryTimeElapsedBits);
185
186 std::vector<GLColor> texData{GLColor::black, GLColor::white, GLColor::white, GLColor::black};
187
188 // Query and texture initialization
189 GLuint texture;
190 GLuint query = 0;
191 glGenQueriesEXT(1, &query);
192 glGenTextures(1, &texture);
193
194 // Upload a texture inside the query
195 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query);
196 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
197 glBindTexture(GL_TEXTURE_2D, texture);
198 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, texData.data());
199 glGenerateMipmap(GL_TEXTURE_2D);
200 glFinish();
201 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
202 ASSERT_GL_NO_ERROR();
203
204 int timeout = 200000;
205 GLuint ready = GL_FALSE;
206 while (ready == GL_FALSE && timeout > 0)
207 {
208 angle::Sleep(0);
209 glGetQueryObjectuivEXT(query, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
210 timeout--;
211 }
212 ASSERT_LT(0, timeout) << "Query result available timed out" << std::endl;
213
214 GLuint64 result = 0;
215 glGetQueryObjectui64vEXT(query, GL_QUERY_RESULT_EXT, &result);
216 ASSERT_GL_NO_ERROR();
217
218 glDeleteTextures(1, &texture);
219 glDeleteQueriesEXT(1, &query);
220
221 std::cout << "Elapsed time: " << result << std::endl;
222 EXPECT_LT(0ul, result);
223
224 // an issue with the queries themselves.
225 EXPECT_LT(result, 1000000000ul);
226 }
227
228 // Tests validation of query functions with respect to elapsed time query
TEST_P(TimerQueriesTest,TimeElapsedValidationTest)229 TEST_P(TimerQueriesTest, TimeElapsedValidationTest)
230 {
231 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_disjoint_timer_query"));
232
233 GLint queryTimeElapsedBits = 0;
234 glGetQueryivEXT(GL_TIME_ELAPSED_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimeElapsedBits);
235 ASSERT_GL_NO_ERROR();
236
237 std::cout << "Time elapsed counter bits: " << queryTimeElapsedBits << std::endl;
238
239 // Skip test if the number of bits is 0
240 ANGLE_SKIP_TEST_IF(!queryTimeElapsedBits);
241
242 GLuint query = 0;
243 glGenQueriesEXT(-1, &query);
244 EXPECT_GL_ERROR(GL_INVALID_VALUE);
245
246 glGenQueriesEXT(1, &query);
247 EXPECT_GL_NO_ERROR();
248
249 glBeginQueryEXT(GL_TIMESTAMP_EXT, query);
250 EXPECT_GL_ERROR(GL_INVALID_ENUM);
251
252 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, 0);
253 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
254
255 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
256 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
257
258 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query);
259 EXPECT_GL_NO_ERROR();
260
261 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query);
262 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
263
264 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
265 EXPECT_GL_NO_ERROR();
266
267 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
268 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
269 }
270
271 // Tests timer queries operating under multiple EGL contexts with mid-query switching
TEST_P(TimerQueriesTest,TimeElapsedMulticontextTest)272 TEST_P(TimerQueriesTest, TimeElapsedMulticontextTest)
273 {
274 // TODO(jmadill): Figure out why this test is flaky on AMD/OpenGL.
275 // http://anglebug.com/1541
276 ANGLE_SKIP_TEST_IF(IsAMD() && IsOpenGL());
277
278 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_disjoint_timer_query"));
279
280 // Test skipped because the Vulkan backend doesn't account for (and remove) time spent in other
281 // contexts.
282 ANGLE_SKIP_TEST_IF(IsVulkan());
283
284 GLint queryTimeElapsedBits = 0;
285 glGetQueryivEXT(GL_TIME_ELAPSED_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimeElapsedBits);
286 ASSERT_GL_NO_ERROR();
287
288 std::cout << "Time elapsed counter bits: " << queryTimeElapsedBits << std::endl;
289
290 // Skip test if the number of bits is 0
291 ANGLE_SKIP_TEST_IF(!queryTimeElapsedBits);
292
293 // Without a glClear, the first draw call on GL takes a huge amount of time when run after the
294 // D3D test on certain NVIDIA drivers
295 glDepthMask(GL_TRUE);
296 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
297
298 EGLint contextAttributes[] = {
299 EGL_CONTEXT_MAJOR_VERSION_KHR,
300 GetParam().majorVersion,
301 EGL_CONTEXT_MINOR_VERSION_KHR,
302 GetParam().minorVersion,
303 EGL_NONE,
304 };
305
306 EGLWindow *window = getEGLWindow();
307
308 EGLDisplay display = window->getDisplay();
309 EGLConfig config = window->getConfig();
310 EGLSurface surface = window->getSurface();
311
312 struct ContextInfo
313 {
314 EGLContext context;
315 GLuint program;
316 GLuint query;
317 EGLDisplay display;
318
319 ContextInfo() : context(EGL_NO_CONTEXT), program(0), query(0), display(EGL_NO_DISPLAY) {}
320
321 ~ContextInfo()
322 {
323 if (context != EGL_NO_CONTEXT && display != EGL_NO_DISPLAY)
324 {
325 eglDestroyContext(display, context);
326 }
327 }
328 };
329 ContextInfo contexts[2];
330
331 constexpr char kCostlyVS[] =
332 "attribute highp vec4 position; varying highp vec4 testPos; void main(void)\n"
333 "{\n"
334 " testPos = position;\n"
335 " gl_Position = position;\n"
336 "}\n";
337
338 constexpr char kCostlyFS[] =
339 "precision highp float; varying highp vec4 testPos; void main(void)\n"
340 "{\n"
341 " vec4 test = testPos;\n"
342 " for (int i = 0; i < 500; i++)\n"
343 " {\n"
344 " test = sqrt(test);\n"
345 " }\n"
346 " gl_FragColor = test;\n"
347 "}\n";
348
349 // Setup the first context with a cheap shader
350 contexts[0].context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes);
351 contexts[0].display = display;
352 ASSERT_NE(contexts[0].context, EGL_NO_CONTEXT);
353 eglMakeCurrent(display, surface, surface, contexts[0].context);
354 contexts[0].program = CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
355 glGenQueriesEXT(1, &contexts[0].query);
356 ASSERT_GL_NO_ERROR();
357
358 // Setup the second context with an expensive shader
359 contexts[1].context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes);
360 contexts[1].display = display;
361 ASSERT_NE(contexts[1].context, EGL_NO_CONTEXT);
362 eglMakeCurrent(display, surface, surface, contexts[1].context);
363 contexts[1].program = CompileProgram(kCostlyVS, kCostlyFS);
364 glGenQueriesEXT(1, &contexts[1].query);
365 ASSERT_GL_NO_ERROR();
366
367 // Start the query and draw a quad on the first context without ending the query
368 eglMakeCurrent(display, surface, surface, contexts[0].context);
369 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, contexts[0].query);
370 drawQuad(contexts[0].program, essl1_shaders::PositionAttrib(), 0.8f);
371 ASSERT_GL_NO_ERROR();
372
373 // Switch contexts, draw the expensive quad and end its query
374 eglMakeCurrent(display, surface, surface, contexts[1].context);
375 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, contexts[1].query);
376 drawQuad(contexts[1].program, "position", 0.8f);
377 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
378 ASSERT_GL_NO_ERROR();
379
380 // Go back to the first context, end its query, and get the result
381 eglMakeCurrent(display, surface, surface, contexts[0].context);
382 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
383
384 GLuint64 result1 = 0;
385 GLuint64 result2 = 0;
386 glGetQueryObjectui64vEXT(contexts[0].query, GL_QUERY_RESULT_EXT, &result1);
387 glDeleteQueriesEXT(1, &contexts[0].query);
388 glDeleteProgram(contexts[0].program);
389 ASSERT_GL_NO_ERROR();
390
391 // Get the 2nd context's results
392 eglMakeCurrent(display, surface, surface, contexts[1].context);
393 glGetQueryObjectui64vEXT(contexts[1].query, GL_QUERY_RESULT_EXT, &result2);
394 glDeleteQueriesEXT(1, &contexts[1].query);
395 glDeleteProgram(contexts[1].program);
396 ASSERT_GL_NO_ERROR();
397
398 // Switch back to main context
399 eglMakeCurrent(display, surface, surface, window->getContext());
400
401 // Compare the results. The cheap quad should be smaller than the expensive one if
402 // virtualization is working correctly
403 std::cout << "Elapsed time: " << result1 << " cheap quad" << std::endl;
404 std::cout << "Elapsed time: " << result2 << " costly quad" << std::endl;
405 EXPECT_LT(0ul, result1);
406 EXPECT_LT(0ul, result2);
407 EXPECT_LT(result1, 1000000000ul);
408 EXPECT_LT(result2, 1000000000ul);
409 EXPECT_LT(result1, result2);
410 }
411
412 // Tests GPU timestamp functionality
TEST_P(TimerQueriesTest,Timestamp)413 TEST_P(TimerQueriesTest, Timestamp)
414 {
415 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_disjoint_timer_query"));
416
417 GLint queryTimestampBits = 0;
418 glGetQueryivEXT(GL_TIMESTAMP_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimestampBits);
419 ASSERT_GL_NO_ERROR();
420
421 std::cout << "Timestamp counter bits: " << queryTimestampBits << std::endl;
422
423 // Macs for some reason return 0 bits so skip the test for now if either are 0
424 ANGLE_SKIP_TEST_IF(!queryTimestampBits);
425
426 glDepthMask(GL_TRUE);
427 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
428
429 GLuint query1 = 0;
430 GLuint query2 = 0;
431 glGenQueriesEXT(1, &query1);
432 glGenQueriesEXT(1, &query2);
433 glQueryCounterEXT(query1, GL_TIMESTAMP_EXT);
434 drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.8f);
435 glQueryCounterEXT(query2, GL_TIMESTAMP_EXT);
436
437 ASSERT_GL_NO_ERROR();
438
439 swapBuffers();
440
441 int timeout = 200000;
442 GLuint ready = GL_FALSE;
443 while (ready == GL_FALSE && timeout > 0)
444 {
445 angle::Sleep(0);
446 glGetQueryObjectuivEXT(query1, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
447 timeout--;
448 }
449 ready = GL_FALSE;
450 while (ready == GL_FALSE && timeout > 0)
451 {
452 angle::Sleep(0);
453 glGetQueryObjectuivEXT(query2, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
454 timeout--;
455 }
456 ASSERT_LT(0, timeout) << "Query result available timed out" << std::endl;
457
458 GLuint64 result1 = 0;
459 GLuint64 result2 = 0;
460 glGetQueryObjectui64vEXT(query1, GL_QUERY_RESULT_EXT, &result1);
461 glGetQueryObjectui64vEXT(query2, GL_QUERY_RESULT_EXT, &result2);
462
463 ASSERT_GL_NO_ERROR();
464
465 glDeleteQueriesEXT(1, &query1);
466 glDeleteQueriesEXT(1, &query2);
467
468 std::cout << "Timestamps: " << result1 << " " << result2 << std::endl;
469 EXPECT_LT(0ul, result1);
470 EXPECT_LT(0ul, result2);
471 EXPECT_LT(result1, result2);
472 }
473
474 class TimerQueriesTestES3 : public TimerQueriesTest
475 {};
476
477 // Tests getting timestamps via glGetInteger64v
TEST_P(TimerQueriesTestES3,TimestampGetInteger64)478 TEST_P(TimerQueriesTestES3, TimestampGetInteger64)
479 {
480 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_disjoint_timer_query"));
481 // http://anglebug.com/4092
482 ANGLE_SKIP_TEST_IF(IsAndroid());
483
484 GLint queryTimestampBits = 0;
485 glGetQueryivEXT(GL_TIMESTAMP_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimestampBits);
486 ASSERT_GL_NO_ERROR();
487
488 std::cout << "Timestamp counter bits: " << queryTimestampBits << std::endl;
489
490 ANGLE_SKIP_TEST_IF(!queryTimestampBits);
491
492 glDepthMask(GL_TRUE);
493 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
494
495 GLint64 result1 = 0;
496 GLint64 result2 = 0;
497 glGetInteger64v(GL_TIMESTAMP_EXT, &result1);
498 drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.8f);
499 glGetInteger64v(GL_TIMESTAMP_EXT, &result2);
500 ASSERT_GL_NO_ERROR();
501 std::cout << "Timestamps (getInteger64v): " << result1 << " " << result2 << std::endl;
502 EXPECT_LT(0l, result1);
503 EXPECT_LT(0l, result2);
504 EXPECT_LT(result1, result2);
505 }
506
507 ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(TimerQueriesTest);
508
509 ANGLE_INSTANTIATE_TEST_ES3(TimerQueriesTestES3);
510