teglMemoryStressTests.cpp revision 3c67e4f0ec73f9c30c6b2ed2adfbfe7faaf576a4
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
46using std::vector;
47using std::string;
48using tcu::TestLog;
49
50using namespace eglw;
51
52namespace deqp
53{
54namespace egl
55{
56
57namespace
58{
59
60enum ObjectType
61{
62	OBJECTTYPE_PBUFFER = (1<<0),
63	OBJECTTYPE_CONTEXT = (1<<1),
64
65//	OBJECTTYPE_WINDOW,
66//	OBJECTTYPE_PIXMAP,
67};
68
69class MemoryAllocator
70{
71public:
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);
76	int				getAllocationCount		(void) const { return (int)(m_pbuffers.size() + m_contexts.size());	}
77	int				getContextCount			(void) const { return (int)m_contexts.size();						}
78	int				getPBufferCount			(void) const { return (int)m_pbuffers.size();						}
79	const string&	getErrorString			(void) const { return m_errorString;							}
80
81private:
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
105MemoryAllocator::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
123MemoryAllocator::~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
138bool 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
182void 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
248void 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
314class MemoryStressCase : public TestCase
315{
316public:
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
332private:
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
344MemoryStressCase::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
356void 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
380void 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
392TestCase::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
467MemoryStressTests::MemoryStressTests (EglTestContext& eglTestCtx)
468	: TestCaseGroup(eglTestCtx, "memory", "Memory allocation stress tests")
469{
470}
471
472void 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