• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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