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