Home | History | Annotate | Download | only in egl
      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);
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    467 MemoryStressTests::MemoryStressTests (EglTestContext& eglTestCtx)
    468 	: TestCaseGroup(eglTestCtx, "memory", "Memory allocation stress tests")
    469 {
    470 }
    471 
    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