1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program EGL Module
3 * ---------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Memory object allocation stress tests
22 *//*--------------------------------------------------------------------*/
23
24 #include "teglMemoryStressTests.hpp"
25
26 #include "tcuTestLog.hpp"
27 #include "tcuCommandLine.hpp"
28
29 #include "deRandom.hpp"
30 #include "deClock.h"
31 #include "deString.h"
32
33 #include "gluDefs.hpp"
34 #include "glwFunctions.hpp"
35 #include "glwDefs.hpp"
36 #include "glwEnums.hpp"
37
38 #include "egluUtil.hpp"
39
40 #include "eglwLibrary.hpp"
41 #include "eglwEnums.hpp"
42
43 #include <vector>
44 #include <string>
45
46 using std::string;
47 using std::vector;
48 using tcu::TestLog;
49
50 using namespace eglw;
51
52 namespace deqp
53 {
54 namespace egl
55 {
56
57 namespace
58 {
59
60 enum ObjectType
61 {
62 OBJECTTYPE_PBUFFER = (1 << 0),
63 OBJECTTYPE_CONTEXT = (1 << 1),
64
65 // OBJECTTYPE_WINDOW,
66 // OBJECTTYPE_PIXMAP,
67 };
68
69 class MemoryAllocator
70 {
71 public:
72 MemoryAllocator(EglTestContext &eglTestCtx, EGLDisplay display, EGLConfig config, int seed, ObjectType types,
73 int minWidth, int minHeight, int maxWidth, int maxHeight, bool use);
74 ~MemoryAllocator(void);
75
76 bool allocateUntilFailure(void);
getAllocationCount(void) const77 int getAllocationCount(void) const
78 {
79 return (int)(m_pbuffers.size() + m_contexts.size());
80 }
getContextCount(void) const81 int getContextCount(void) const
82 {
83 return (int)m_contexts.size();
84 }
getPBufferCount(void) const85 int getPBufferCount(void) const
86 {
87 return (int)m_pbuffers.size();
88 }
getErrorString(void) const89 const string &getErrorString(void) const
90 {
91 return m_errorString;
92 }
93
94 private:
95 void allocatePBuffer(void);
96 void allocateContext(void);
97
98 EglTestContext &m_eglTestCtx;
99 EGLDisplay m_display;
100 EGLConfig m_config;
101 glw::Functions m_gl;
102
103 de::Random m_rnd;
104 bool m_failed;
105 string m_errorString;
106
107 ObjectType m_types;
108 int m_minWidth;
109 int m_minHeight;
110 int m_maxWidth;
111 int m_maxHeight;
112 bool m_use;
113
114 vector<EGLSurface> m_pbuffers;
115 vector<EGLContext> m_contexts;
116 };
117
MemoryAllocator(EglTestContext & eglTestCtx,EGLDisplay display,EGLConfig config,int seed,ObjectType types,int minWidth,int minHeight,int maxWidth,int maxHeight,bool use)118 MemoryAllocator::MemoryAllocator(EglTestContext &eglTestCtx, EGLDisplay display, EGLConfig config, int seed,
119 ObjectType types, int minWidth, int minHeight, int maxWidth, int maxHeight, bool use)
120 : m_eglTestCtx(eglTestCtx)
121 , m_display(display)
122 , m_config(config)
123
124 , m_rnd(seed)
125 , m_failed(false)
126
127 , m_types(types)
128 , m_minWidth(minWidth)
129 , m_minHeight(minHeight)
130 , m_maxWidth(maxWidth)
131 , m_maxHeight(maxHeight)
132 , m_use(use)
133 {
134 m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2, 0));
135 }
136
~MemoryAllocator(void)137 MemoryAllocator::~MemoryAllocator(void)
138 {
139 const Library &egl = m_eglTestCtx.getLibrary();
140
141 for (vector<EGLSurface>::const_iterator iter = m_pbuffers.begin(); iter != m_pbuffers.end(); ++iter)
142 egl.destroySurface(m_display, *iter);
143
144 m_pbuffers.clear();
145
146 for (vector<EGLContext>::const_iterator iter = m_contexts.begin(); iter != m_contexts.end(); ++iter)
147 egl.destroyContext(m_display, *iter);
148
149 m_contexts.clear();
150 }
151
allocateUntilFailure(void)152 bool MemoryAllocator::allocateUntilFailure(void)
153 {
154 const uint64_t timeLimitUs = 10000000; // 10s
155 uint64_t beginTimeUs = deGetMicroseconds();
156 vector<ObjectType> types;
157
158 if ((m_types & OBJECTTYPE_CONTEXT) != 0)
159 types.push_back(OBJECTTYPE_CONTEXT);
160
161 if ((m_types & OBJECTTYPE_PBUFFER) != 0)
162 types.push_back(OBJECTTYPE_PBUFFER);
163
164 // If objects should be used. Create one of both at beginning to allow using them.
165 if (m_contexts.size() == 0 && m_pbuffers.size() == 0 && m_use)
166 {
167 allocateContext();
168 allocatePBuffer();
169 }
170
171 while (!m_failed)
172 {
173 ObjectType type = m_rnd.choose<ObjectType>(types.begin(), types.end());
174
175 switch (type)
176 {
177 case OBJECTTYPE_PBUFFER:
178 allocatePBuffer();
179 break;
180
181 case OBJECTTYPE_CONTEXT:
182 allocateContext();
183 break;
184
185 default:
186 DE_ASSERT(false);
187 }
188
189 if (deGetMicroseconds() - beginTimeUs > timeLimitUs)
190 return true;
191 }
192
193 return false;
194 }
195
allocatePBuffer(void)196 void MemoryAllocator::allocatePBuffer(void)
197 {
198 // Reserve space for new allocations
199 try
200 {
201 m_pbuffers.reserve(m_pbuffers.size() + 1);
202 }
203 catch (const std::bad_alloc &)
204 {
205 m_errorString = "std::bad_alloc when allocating more space for testcase. Out of host memory.";
206 m_failed = true;
207 return;
208 }
209
210 // Allocate pbuffer
211 try
212 {
213 const Library &egl = m_eglTestCtx.getLibrary();
214 const EGLint width = m_rnd.getInt(m_minWidth, m_maxWidth);
215 const EGLint height = m_rnd.getInt(m_minHeight, m_maxHeight);
216 const EGLint attribList[] = {EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE};
217
218 EGLSurface surface = egl.createPbufferSurface(m_display, m_config, attribList);
219 EGLU_CHECK_MSG(egl, "eglCreatePbufferSurface");
220
221 DE_ASSERT(surface != EGL_NO_SURFACE);
222
223 m_pbuffers.push_back(surface);
224
225 if (m_use && m_contexts.size() > 0)
226 {
227 EGLContext context = m_rnd.choose<EGLContext>(m_contexts.begin(), m_contexts.end());
228 const float red = m_rnd.getFloat();
229 const float green = m_rnd.getFloat();
230 const float blue = m_rnd.getFloat();
231 const float alpha = m_rnd.getFloat();
232
233 EGLU_CHECK_CALL(egl, makeCurrent(m_display, surface, surface, context));
234
235 m_gl.clearColor(red, green, blue, alpha);
236 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glClearColor()");
237
238 m_gl.clear(GL_COLOR_BUFFER_BIT);
239 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glClear()");
240
241 EGLU_CHECK_CALL(egl, makeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
242 }
243 }
244 catch (const eglu::Error &error)
245 {
246 if (error.getError() == EGL_BAD_ALLOC)
247 {
248 m_errorString = "eglCreatePbufferSurface returned EGL_BAD_ALLOC";
249 m_failed = true;
250 return;
251 }
252 else
253 throw;
254 }
255 }
256
allocateContext(void)257 void MemoryAllocator::allocateContext(void)
258 {
259 // Reserve space for new allocations
260 try
261 {
262 m_contexts.reserve(m_contexts.size() + 1);
263 }
264 catch (const std::bad_alloc &)
265 {
266 m_errorString = "std::bad_alloc when allocating more space for testcase. Out of host memory.";
267 m_failed = true;
268 return;
269 }
270
271 // Allocate context
272 try
273 {
274 const Library &egl = m_eglTestCtx.getLibrary();
275 const EGLint attribList[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
276
277 EGLU_CHECK_CALL(egl, bindAPI(EGL_OPENGL_ES_API));
278 EGLContext context = egl.createContext(m_display, m_config, EGL_NO_CONTEXT, attribList);
279 EGLU_CHECK_MSG(egl, "eglCreateContext");
280
281 DE_ASSERT(context != EGL_NO_CONTEXT);
282
283 m_contexts.push_back(context);
284
285 if (m_use && m_pbuffers.size() > 0)
286 {
287 EGLSurface surface = m_rnd.choose<EGLSurface>(m_pbuffers.begin(), m_pbuffers.end());
288 const float red = m_rnd.getFloat();
289 const float green = m_rnd.getFloat();
290 const float blue = m_rnd.getFloat();
291 const float alpha = m_rnd.getFloat();
292
293 EGLU_CHECK_CALL(egl, makeCurrent(m_display, surface, surface, context));
294
295 m_gl.clearColor(red, green, blue, alpha);
296 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glClearColor()");
297
298 m_gl.clear(GL_COLOR_BUFFER_BIT);
299 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glClear()");
300
301 EGLU_CHECK_CALL(egl, makeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
302 }
303 }
304 catch (const eglu::Error &error)
305 {
306 if (error.getError() == EGL_BAD_ALLOC)
307 {
308 m_errorString = "eglCreateContext returned EGL_BAD_ALLOC";
309 m_failed = true;
310 return;
311 }
312 else
313 throw;
314 }
315 }
316
317 } // namespace
318
319 class MemoryStressCase : public TestCase
320 {
321 public:
322 struct Spec
323 {
324 ObjectType types;
325 int minWidth;
326 int minHeight;
327 int maxWidth;
328 int maxHeight;
329 bool use;
330 };
331
332 MemoryStressCase(EglTestContext &eglTestCtx, Spec spec, const char *name, const char *description);
333 void init(void);
334 void deinit(void);
335 IterateResult iterate(void);
336
337 private:
338 Spec m_spec;
339 vector<int> m_allocationCounts;
340 MemoryAllocator *m_allocator;
341
342 int m_iteration;
343 int m_iterationCount;
344 int m_seed;
345 EGLDisplay m_display;
346 EGLConfig m_config;
347 };
348
MemoryStressCase(EglTestContext & eglTestCtx,Spec spec,const char * name,const char * description)349 MemoryStressCase::MemoryStressCase(EglTestContext &eglTestCtx, Spec spec, const char *name, const char *description)
350 : TestCase(eglTestCtx, name, description)
351 , m_spec(spec)
352 , m_allocator(NULL)
353 , m_iteration(0)
354 , m_iterationCount(10)
355 , m_seed(deStringHash(name))
356 , m_display(EGL_NO_DISPLAY)
357 , m_config(DE_NULL)
358 {
359 }
360
init(void)361 void MemoryStressCase::init(void)
362 {
363 const Library &egl = m_eglTestCtx.getLibrary();
364 EGLint configCount = 0;
365 const EGLint attribList[] = {EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE};
366
367 if (!m_testCtx.getCommandLine().isOutOfMemoryTestEnabled())
368 {
369 m_testCtx.getLog()
370 << TestLog::Message
371 << "Tests that exhaust memory are disabled, use --deqp-test-oom=enable command line option to enable."
372 << TestLog::EndMessage;
373 throw tcu::NotSupportedError("OOM tests disabled");
374 }
375
376 m_display = eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay());
377
378 EGLU_CHECK_CALL(egl, chooseConfig(m_display, attribList, &m_config, 1, &configCount));
379
380 TCU_CHECK(configCount != 0);
381 }
382
deinit(void)383 void MemoryStressCase::deinit(void)
384 {
385 delete m_allocator;
386 m_allocator = DE_NULL;
387
388 if (m_display != EGL_NO_DISPLAY)
389 {
390 m_eglTestCtx.getLibrary().terminate(m_display);
391 m_display = EGL_NO_DISPLAY;
392 }
393 }
394
iterate(void)395 TestCase::IterateResult MemoryStressCase::iterate(void)
396 {
397 TestLog &log = m_testCtx.getLog();
398
399 if (m_iteration < m_iterationCount)
400 {
401 try
402 {
403 if (!m_allocator)
404 m_allocator =
405 new MemoryAllocator(m_eglTestCtx, m_display, m_config, m_seed, m_spec.types, m_spec.minWidth,
406 m_spec.minHeight, m_spec.maxWidth, m_spec.maxHeight, m_spec.use);
407
408 if (m_allocator->allocateUntilFailure())
409 {
410 log << TestLog::Message << "Couldn't exhaust memory before timeout. Allocated "
411 << m_allocator->getAllocationCount() << " objects." << TestLog::EndMessage;
412 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
413
414 delete m_allocator;
415 m_allocator = NULL;
416
417 return STOP;
418 }
419
420 log << TestLog::Message << "Iteration " << m_iteration << ": Allocated "
421 << m_allocator->getAllocationCount() << " objects; " << m_allocator->getContextCount() << " contexts, "
422 << m_allocator->getPBufferCount() << " PBuffers." << TestLog::EndMessage;
423 log << TestLog::Message << "Got expected error: " << m_allocator->getErrorString() << TestLog::EndMessage;
424 m_allocationCounts.push_back(m_allocator->getAllocationCount());
425
426 delete m_allocator;
427 m_allocator = NULL;
428
429 m_iteration++;
430
431 return CONTINUE;
432 }
433 catch (...)
434 {
435 log << TestLog::Message << "Iteration " << m_iteration << ": Allocated "
436 << m_allocator->getAllocationCount() << " objects; " << m_allocator->getContextCount() << " contexts, "
437 << m_allocator->getPBufferCount() << " PBuffers." << TestLog::EndMessage;
438 log << TestLog::Message << "Unexpected error" << TestLog::EndMessage;
439 throw;
440 }
441 }
442 else
443 {
444 // Analyze number of passed allocations.
445 int min = m_allocationCounts[0];
446 int max = m_allocationCounts[0];
447
448 float threshold = 50.0f;
449
450 for (int allocNdx = 0; allocNdx < (int)m_allocationCounts.size(); allocNdx++)
451 {
452 min = deMin32(m_allocationCounts[allocNdx], min);
453 max = deMax32(m_allocationCounts[allocNdx], max);
454 }
455
456 if (min == 0 && max != 0)
457 {
458 log << TestLog::Message << "Allocation count zero" << TestLog::EndMessage;
459 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
460 }
461 else
462 {
463 float change = (float)(min - max) / ((float)(max));
464
465 if (change > threshold)
466 {
467 log << TestLog::Message << "Allocated objects max: " << max << ", min: " << min
468 << ", difference: " << change << "% threshold: " << threshold << "%" << TestLog::EndMessage;
469 m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Allocation count variation");
470 }
471 else
472 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
473 }
474
475 return STOP;
476 }
477 }
478
MemoryStressTests(EglTestContext & eglTestCtx)479 MemoryStressTests::MemoryStressTests(EglTestContext &eglTestCtx)
480 : TestCaseGroup(eglTestCtx, "memory", "Memory allocation stress tests")
481 {
482 }
483
init(void)484 void MemoryStressTests::init(void)
485 {
486 // Check small pbuffers 256x256
487 {
488 MemoryStressCase::Spec spec;
489
490 spec.types = OBJECTTYPE_PBUFFER;
491 spec.minWidth = 256;
492 spec.minHeight = 256;
493 spec.maxWidth = 256;
494 spec.maxHeight = 256;
495 spec.use = false;
496
497 addChild(new MemoryStressCase(m_eglTestCtx, spec, "pbuffer_256x256", "PBuffer allocation stress tests"));
498 }
499
500 // Check small pbuffers 256x256 and use them
501 {
502 MemoryStressCase::Spec spec;
503
504 spec.types = OBJECTTYPE_PBUFFER;
505 spec.minWidth = 256;
506 spec.minHeight = 256;
507 spec.maxWidth = 256;
508 spec.maxHeight = 256;
509 spec.use = true;
510
511 addChild(new MemoryStressCase(m_eglTestCtx, spec, "pbuffer_256x256_use", "PBuffer allocation stress tests"));
512 }
513
514 // Check big pbuffers 1024x1024
515 {
516 MemoryStressCase::Spec spec;
517
518 spec.types = OBJECTTYPE_PBUFFER;
519 spec.minWidth = 1024;
520 spec.minHeight = 1024;
521 spec.maxWidth = 1024;
522 spec.maxHeight = 1024;
523 spec.use = false;
524
525 addChild(new MemoryStressCase(m_eglTestCtx, spec, "pbuffer_1024x1024", "PBuffer allocation stress tests"));
526 }
527
528 // Check big pbuffers 1024x1024 and use them
529 {
530 MemoryStressCase::Spec spec;
531
532 spec.types = OBJECTTYPE_PBUFFER;
533 spec.minWidth = 1024;
534 spec.minHeight = 1024;
535 spec.maxWidth = 1024;
536 spec.maxHeight = 1024;
537 spec.use = true;
538
539 addChild(new MemoryStressCase(m_eglTestCtx, spec, "pbuffer_1024x1024_use", "PBuffer allocation stress tests"));
540 }
541
542 // Check different sized pbuffers
543 {
544 MemoryStressCase::Spec spec;
545
546 spec.types = OBJECTTYPE_PBUFFER;
547 spec.minWidth = 64;
548 spec.minHeight = 64;
549 spec.maxWidth = 1024;
550 spec.maxHeight = 1024;
551 spec.use = false;
552
553 addChild(new MemoryStressCase(m_eglTestCtx, spec, "pbuffer", "PBuffer allocation stress tests"));
554 }
555
556 // Check different sized pbuffers and use them
557 {
558 MemoryStressCase::Spec spec;
559
560 spec.types = OBJECTTYPE_PBUFFER;
561 spec.minWidth = 64;
562 spec.minHeight = 64;
563 spec.maxWidth = 1024;
564 spec.maxHeight = 1024;
565 spec.use = true;
566
567 addChild(new MemoryStressCase(m_eglTestCtx, spec, "pbuffer_use", "PBuffer allocation stress tests"));
568 }
569
570 // Check contexts
571 {
572 MemoryStressCase::Spec spec;
573
574 spec.types = OBJECTTYPE_CONTEXT;
575 spec.minWidth = 1024;
576 spec.minHeight = 1024;
577 spec.maxWidth = 1024;
578 spec.maxHeight = 1024;
579 spec.use = false;
580
581 addChild(new MemoryStressCase(m_eglTestCtx, spec, "context", "Context allocation stress tests"));
582 }
583
584 // Check contexts and use them
585 {
586 MemoryStressCase::Spec spec;
587
588 spec.types = OBJECTTYPE_CONTEXT;
589 spec.minWidth = 1024;
590 spec.minHeight = 1024;
591 spec.maxWidth = 1024;
592 spec.maxHeight = 1024;
593 spec.use = true;
594
595 addChild(new MemoryStressCase(m_eglTestCtx, spec, "context_use", "Context allocation stress tests"));
596 }
597
598 // Check contexts and pbuffers
599 {
600 MemoryStressCase::Spec spec;
601
602 spec.types = (ObjectType)(OBJECTTYPE_PBUFFER | OBJECTTYPE_CONTEXT);
603 spec.minWidth = 64;
604 spec.minHeight = 64;
605 spec.maxWidth = 1024;
606 spec.maxHeight = 1024;
607 spec.use = false;
608
609 addChild(
610 new MemoryStressCase(m_eglTestCtx, spec, "pbuffer_context", "PBuffer and context allocation stress tests"));
611 }
612
613 // Check contexts and pbuffers and use
614 {
615 MemoryStressCase::Spec spec;
616
617 spec.types = (ObjectType)(OBJECTTYPE_PBUFFER | OBJECTTYPE_CONTEXT);
618 spec.minWidth = 64;
619 spec.minHeight = 64;
620 spec.maxWidth = 1024;
621 spec.maxHeight = 1024;
622 spec.use = true;
623
624 addChild(new MemoryStressCase(m_eglTestCtx, spec, "pbuffer_context_use",
625 "PBuffer and context allocation stress tests"));
626 }
627 }
628
629 } // namespace egl
630 } // namespace deqp
631