xref: /aosp_15_r20/external/deqp/modules/egl/teglMemoryStressTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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