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