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