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 = (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