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 Multi threaded EGL tests
22  *//*--------------------------------------------------------------------*/
23 #include "teglMultiThreadTests.hpp"
24 
25 #include "egluNativeWindow.hpp"
26 #include "egluNativePixmap.hpp"
27 #include "egluUtil.hpp"
28 
29 #include "tcuTestLog.hpp"
30 #include "tcuCommandLine.hpp"
31 
32 #include "deRandom.hpp"
33 
34 #include "deThread.hpp"
35 #include "deMutex.hpp"
36 #include "deSemaphore.hpp"
37 
38 #include "deAtomic.h"
39 #include "deClock.h"
40 
41 #include "eglwLibrary.hpp"
42 #include "eglwEnums.hpp"
43 
44 #include <vector>
45 #include <set>
46 #include <string>
47 #include <sstream>
48 
49 using std::vector;
50 using std::string;
51 using std::pair;
52 using std::set;
53 using std::ostringstream;
54 
55 using namespace eglw;
56 
57 namespace deqp
58 {
59 namespace egl
60 {
61 
62 class ThreadLog
63 {
64 public:
65 	class BeginMessageToken	{};
66 	class EndMessageToken	{};
67 
68 	struct Message
69 	{
Messagedeqp::egl::ThreadLog::Message70 					Message	(deUint64 timeUs_, const char* msg_) : timeUs(timeUs_), msg(msg_) {}
71 
72 		deUint64	timeUs;
73 		string		msg;
74 	};
75 
ThreadLog(void)76 								ThreadLog	(void)						{ m_messages.reserve(100); }
77 
operator <<(const BeginMessageToken &)78 	ThreadLog&					operator<<	(const BeginMessageToken&)	{ return *this; }
79 	ThreadLog&					operator<<	(const EndMessageToken&);
80 
81 	template<class T>
operator <<(const T & t)82 	ThreadLog&					operator<<	(const T& t)				{ m_message << t; return *this; }
getMessages(void) const83 	const vector<Message>&		getMessages (void) const				{ return m_messages; }
84 
85 	static BeginMessageToken	BeginMessage;
86 	static EndMessageToken		EndMessage;
87 
88 private:
89 	ostringstream		m_message;
90 	vector<Message>		m_messages;
91 };
92 
operator <<(const EndMessageToken &)93 ThreadLog& ThreadLog::operator<< (const EndMessageToken&)
94 {
95 	m_messages.push_back(Message(deGetMicroseconds(), m_message.str().c_str()));
96 	m_message.str("");
97 	return *this;
98 }
99 
100 ThreadLog::BeginMessageToken	ThreadLog::BeginMessage;
101 ThreadLog::EndMessageToken		ThreadLog::EndMessage;
102 
103 class MultiThreadedTest;
104 
105 class TestThread : public de::Thread
106 {
107 public:
108 	enum ThreadStatus
109 	{
110 		THREADSTATUS_NOT_STARTED = 0,
111 		THREADSTATUS_RUNNING,
112 		THREADSTATUS_READY,
113 	};
114 
115 					TestThread	(MultiThreadedTest& test, int id);
116 	void			run			(void);
117 
getStatus(void) const118 	ThreadStatus	getStatus	(void) const	{ return m_status; }
getLog(void)119 	ThreadLog&		getLog		(void)			{ return m_log; }
120 
getId(void) const121 	int				getId		(void) const	{ return m_id; }
122 
setStatus(ThreadStatus status)123 	void			setStatus	(ThreadStatus status)	{ m_status = status; }
124 
125 	const Library&	getLibrary	(void) const;
126 
127 	// Test has stopped
128 	class TestStop {};
129 
130 
131 private:
132 	MultiThreadedTest&	m_test;
133 	const int			m_id;
134 	ThreadStatus		m_status;
135 	ThreadLog			m_log;
136 };
137 
138 class MultiThreadedTest : public TestCase
139 {
140 public:
141 							MultiThreadedTest	(EglTestContext& eglTestCtx, const char* name, const char* description, int threadCount, deUint64 timeoutUs);
142 	virtual					~MultiThreadedTest	(void);
143 
144 	void					init				(void);
145 	void					deinit				(void);
146 
147 	virtual bool			runThread			(TestThread& thread) = 0;
148 	virtual IterateResult	iterate				(void);
149 	void					execTest			(TestThread& thread);
150 
getLibrary(void) const151 	const Library&			getLibrary			(void) const { return m_eglTestCtx.getLibrary(); }
152 
153 protected:
154 	void					barrier				(void);
155 
156 private:
157 	int						m_threadCount;
158 	bool					m_initialized;
159 	deUint64				m_startTimeUs;
160 	const deUint64			m_timeoutUs;
161 	bool					m_ok;
162 	bool					m_supported;
163 	vector<TestThread*>		m_threads;
164 
165 	volatile deInt32		m_barrierWaiters;
166 	de::Semaphore			m_barrierSemaphore1;
167 	de::Semaphore			m_barrierSemaphore2;
168 
169 protected:
170 	EGLDisplay				m_display;
171 };
172 
getLibrary(void) const173 inline const Library& TestThread::getLibrary (void) const
174 {
175 	return m_test.getLibrary();
176 }
177 
TestThread(MultiThreadedTest & test,int id)178 TestThread::TestThread (MultiThreadedTest& test, int id)
179 	: m_test	(test)
180 	, m_id		(id)
181 	, m_status	(THREADSTATUS_NOT_STARTED)
182 {
183 }
184 
run(void)185 void TestThread::run (void)
186 {
187 	m_status = THREADSTATUS_RUNNING;
188 
189 	try
190 	{
191 		m_test.execTest(*this);
192 	}
193 	catch (const TestThread::TestStop&)
194 	{
195 		getLog() << ThreadLog::BeginMessage << "Thread stopped" << ThreadLog::EndMessage;
196 	}
197 	catch (const tcu::NotSupportedError& e)
198 	{
199 		getLog() << ThreadLog::BeginMessage << "Not supported: '" << e.what() << "'" << ThreadLog::EndMessage;
200 	}
201 	catch (const std::exception& e)
202 	{
203 		getLog() << ThreadLog::BeginMessage << "Got exception: '" << e.what() << "'" << ThreadLog::EndMessage;
204 	}
205 	catch (...)
206 	{
207 		getLog() << ThreadLog::BeginMessage << "Unknown exception" << ThreadLog::EndMessage;
208 	}
209 
210 	getLibrary().releaseThread();
211 	m_status = THREADSTATUS_READY;
212 }
213 
execTest(TestThread & thread)214 void MultiThreadedTest::execTest (TestThread& thread)
215 {
216 	try
217 	{
218 		if (!runThread(thread))
219 			m_ok = false;
220 	}
221 	catch (const TestThread::TestStop&)
222 	{
223 		// Thread exited due to error in other thread
224 		throw;
225 	}
226 	catch (const tcu::NotSupportedError&)
227 	{
228 		m_supported = false;
229 
230 		// Release barriers
231 		for (int threadNdx = 0; threadNdx < (int)m_threads.size(); threadNdx++)
232 		{
233 			m_barrierSemaphore1.increment();
234 			m_barrierSemaphore2.increment();
235 		}
236 
237 		throw;
238 	}
239 	catch(...)
240 	{
241 		m_ok = false;
242 
243 		// Release barriers
244 		for (int threadNdx = 0; threadNdx < (int)m_threads.size(); threadNdx++)
245 		{
246 			m_barrierSemaphore1.increment();
247 			m_barrierSemaphore2.increment();
248 		}
249 
250 		throw;
251 	}
252 }
253 
MultiThreadedTest(EglTestContext & eglTestCtx,const char * name,const char * description,int threadCount,deUint64 timeoutUs)254 MultiThreadedTest::MultiThreadedTest (EglTestContext& eglTestCtx, const char* name, const char* description, int threadCount, deUint64 timeoutUs)
255 	: TestCase				(eglTestCtx, name, description)
256 	, m_threadCount			(threadCount)
257 	, m_initialized			(false)
258 	, m_startTimeUs			(0)
259 	, m_timeoutUs			(timeoutUs)
260 	, m_ok					(true)
261 	, m_supported			(true)
262 	, m_barrierWaiters		(0)
263 	, m_barrierSemaphore1	(0, 0)
264 	, m_barrierSemaphore2	(1, 0)
265 
266 	, m_display				(EGL_NO_DISPLAY)
267 {
268 }
269 
~MultiThreadedTest(void)270 MultiThreadedTest::~MultiThreadedTest (void)
271 {
272 	for (int threadNdx = 0; threadNdx < (int)m_threads.size(); threadNdx++)
273 		delete m_threads[threadNdx];
274 	m_threads.clear();
275 }
276 
init(void)277 void MultiThreadedTest::init (void)
278 {
279 	m_display = eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay());
280 }
281 
deinit(void)282 void MultiThreadedTest::deinit (void)
283 {
284 	if (m_display != EGL_NO_DISPLAY)
285 	{
286 		m_eglTestCtx.getLibrary().terminate(m_display);
287 		m_display = EGL_NO_DISPLAY;
288 	}
289 }
290 
barrier(void)291 void MultiThreadedTest::barrier (void)
292 {
293 	{
294 		const deInt32 waiters = deAtomicIncrement32(&m_barrierWaiters);
295 
296 		if (waiters == m_threadCount)
297 		{
298 			m_barrierSemaphore2.decrement();
299 			m_barrierSemaphore1.increment();
300 		}
301 		else
302 		{
303 			m_barrierSemaphore1.decrement();
304 			m_barrierSemaphore1.increment();
305 		}
306 	}
307 
308 	{
309 		const deInt32 waiters = deAtomicDecrement32(&m_barrierWaiters);
310 
311 		if (waiters == 0)
312 		{
313 			m_barrierSemaphore1.decrement();
314 			m_barrierSemaphore2.increment();
315 		}
316 		else
317 		{
318 			m_barrierSemaphore2.decrement();
319 			m_barrierSemaphore2.increment();
320 		}
321 	}
322 
323 	// Barrier was released due an error in other thread
324 	if (!m_ok || !m_supported)
325 		throw TestThread::TestStop();
326 }
327 
iterate(void)328 TestCase::IterateResult MultiThreadedTest::iterate (void)
329 {
330 	if (!m_initialized)
331 	{
332 		m_testCtx.getLog() << tcu::TestLog::Message << "Thread timeout limit: " << m_timeoutUs << "us" << tcu::TestLog::EndMessage;
333 
334 		m_ok = true;
335 		m_supported = true;
336 
337 		// Create threads
338 		m_threads.reserve(m_threadCount);
339 
340 		for (int threadNdx = 0; threadNdx < m_threadCount; threadNdx++)
341 			m_threads.push_back(new TestThread(*this, threadNdx));
342 
343 		m_startTimeUs = deGetMicroseconds();
344 
345 		// Run threads
346 		for (int threadNdx = 0; threadNdx < (int)m_threads.size(); threadNdx++)
347 			m_threads[threadNdx]->start();
348 
349 		m_initialized = true;
350 	}
351 
352 	int readyCount = 0;
353 	for (int threadNdx = 0; threadNdx < (int)m_threads.size(); threadNdx++)
354 	{
355 		if (m_threads[threadNdx]->getStatus() != TestThread::THREADSTATUS_RUNNING)
356 			readyCount++;
357 	}
358 
359 	if (readyCount == m_threadCount)
360 	{
361 		// Join threads
362 		for (int threadNdx = 0; threadNdx < (int)m_threads.size(); threadNdx++)
363 			m_threads[threadNdx]->join();
364 
365 		// Get logs
366 		{
367 			vector<int> messageNdx;
368 
369 			messageNdx.resize(m_threads.size(), 0);
370 
371 			while (true)
372 			{
373 				int			nextThreadNdx		= -1;
374 				deUint64	nextThreadTimeUs	= 0;
375 
376 				for (int threadNdx = 0; threadNdx < (int)m_threads.size(); threadNdx++)
377 				{
378 					if (messageNdx[threadNdx] >= (int)m_threads[threadNdx]->getLog().getMessages().size())
379 						continue;
380 
381 					if (nextThreadNdx == -1 || nextThreadTimeUs > m_threads[threadNdx]->getLog().getMessages()[messageNdx[threadNdx]].timeUs)
382 					{
383 						nextThreadNdx		= threadNdx;
384 						nextThreadTimeUs	= m_threads[threadNdx]->getLog().getMessages()[messageNdx[threadNdx]].timeUs;
385 					}
386 				}
387 
388 				if (nextThreadNdx == -1)
389 					break;
390 
391 				m_testCtx.getLog() << tcu::TestLog::Message << "[" << (nextThreadTimeUs - m_startTimeUs) << "] (" << nextThreadNdx << ") " << m_threads[nextThreadNdx]->getLog().getMessages()[messageNdx[nextThreadNdx]].msg << tcu::TestLog::EndMessage;
392 
393 				messageNdx[nextThreadNdx]++;
394 			}
395 		}
396 
397 		// Destroy threads
398 		for (int threadNdx = 0; threadNdx < (int)m_threads.size(); threadNdx++)
399 			delete m_threads[threadNdx];
400 
401 		m_threads.clear();
402 
403 		// Set result
404 		if (m_ok)
405 		{
406 			if (!m_supported)
407 				m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported");
408 			else
409 				m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
410 		}
411 		else
412 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
413 
414 		return STOP;
415 	}
416 	else
417 	{
418 		// Check for timeout
419 		const deUint64 currentTimeUs = deGetMicroseconds();
420 
421 		if (currentTimeUs - m_startTimeUs > m_timeoutUs)
422 		{
423 			// Get logs
424 			{
425 				vector<int> messageNdx;
426 
427 				messageNdx.resize(m_threads.size(), 0);
428 
429 				while (true)
430 				{
431 					int			nextThreadNdx		= -1;
432 					deUint64	nextThreadTimeUs	= 0;
433 
434 					for (int threadNdx = 0; threadNdx < (int)m_threads.size(); threadNdx++)
435 					{
436 						if (messageNdx[threadNdx] >= (int)m_threads[threadNdx]->getLog().getMessages().size())
437 							continue;
438 
439 						if (nextThreadNdx == -1 || nextThreadTimeUs > m_threads[threadNdx]->getLog().getMessages()[messageNdx[threadNdx]].timeUs)
440 						{
441 							nextThreadNdx		= threadNdx;
442 							nextThreadTimeUs	= m_threads[threadNdx]->getLog().getMessages()[messageNdx[threadNdx]].timeUs;
443 						}
444 					}
445 
446 					if (nextThreadNdx == -1)
447 						break;
448 
449 					m_testCtx.getLog() << tcu::TestLog::Message << "[" << (nextThreadTimeUs - m_startTimeUs) << "] (" << nextThreadNdx << ") " << m_threads[nextThreadNdx]->getLog().getMessages()[messageNdx[nextThreadNdx]].msg << tcu::TestLog::EndMessage;
450 
451 					messageNdx[nextThreadNdx]++;
452 				}
453 			}
454 
455 			m_testCtx.getLog() << tcu::TestLog::Message << "[" << (currentTimeUs - m_startTimeUs) << "] (-) Timeout, Limit: " << m_timeoutUs << "us" << tcu::TestLog::EndMessage;
456 			m_testCtx.getLog() << tcu::TestLog::Message << "[" << (currentTimeUs - m_startTimeUs) << "] (-) Trying to perform resource cleanup..." << tcu::TestLog::EndMessage;
457 
458 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
459 			return STOP;
460 		}
461 
462 		// Sleep
463 		deSleep(10);
464 	}
465 
466 	return CONTINUE;
467 }
468 
469 namespace
470 {
471 
configAttributeToString(EGLint e)472 const char* configAttributeToString (EGLint e)
473 {
474 	switch (e)
475 	{
476 		case EGL_BUFFER_SIZE:				return "EGL_BUFFER_SIZE";
477 		case EGL_RED_SIZE:					return "EGL_RED_SIZE";
478 		case EGL_GREEN_SIZE:				return "EGL_GREEN_SIZE";
479 		case EGL_BLUE_SIZE:					return "EGL_BLUE_SIZE";
480 		case EGL_LUMINANCE_SIZE:			return "EGL_LUMINANCE_SIZE";
481 		case EGL_ALPHA_SIZE:				return "EGL_ALPHA_SIZE";
482 		case EGL_ALPHA_MASK_SIZE:			return "EGL_ALPHA_MASK_SIZE";
483 		case EGL_BIND_TO_TEXTURE_RGB:		return "EGL_BIND_TO_TEXTURE_RGB";
484 		case EGL_BIND_TO_TEXTURE_RGBA:		return "EGL_BIND_TO_TEXTURE_RGBA";
485 		case EGL_COLOR_BUFFER_TYPE:			return "EGL_COLOR_BUFFER_TYPE";
486 		case EGL_CONFIG_CAVEAT:				return "EGL_CONFIG_CAVEAT";
487 		case EGL_CONFIG_ID:					return "EGL_CONFIG_ID";
488 		case EGL_CONFORMANT:				return "EGL_CONFORMANT";
489 		case EGL_DEPTH_SIZE:				return "EGL_DEPTH_SIZE";
490 		case EGL_LEVEL:						return "EGL_LEVEL";
491 		case EGL_MAX_PBUFFER_WIDTH:			return "EGL_MAX_PBUFFER_WIDTH";
492 		case EGL_MAX_PBUFFER_HEIGHT:		return "EGL_MAX_PBUFFER_HEIGHT";
493 		case EGL_MAX_PBUFFER_PIXELS:		return "EGL_MAX_PBUFFER_PIXELS";
494 		case EGL_MAX_SWAP_INTERVAL:			return "EGL_MAX_SWAP_INTERVAL";
495 		case EGL_MIN_SWAP_INTERVAL:			return "EGL_MIN_SWAP_INTERVAL";
496 		case EGL_NATIVE_RENDERABLE:			return "EGL_NATIVE_RENDERABLE";
497 		case EGL_NATIVE_VISUAL_ID:			return "EGL_NATIVE_VISUAL_ID";
498 		case EGL_NATIVE_VISUAL_TYPE:		return "EGL_NATIVE_VISUAL_TYPE";
499 		case EGL_RENDERABLE_TYPE:			return "EGL_RENDERABLE_TYPE";
500 		case EGL_SAMPLE_BUFFERS:			return "EGL_SAMPLE_BUFFERS";
501 		case EGL_SAMPLES:					return "EGL_SAMPLES";
502 		case EGL_STENCIL_SIZE:				return "EGL_STENCIL_SIZE";
503 		case EGL_SURFACE_TYPE:				return "EGL_SURFACE_TYPE";
504 		case EGL_TRANSPARENT_TYPE:			return "EGL_TRANSPARENT_TYPE";
505 		case EGL_TRANSPARENT_RED_VALUE:		return "EGL_TRANSPARENT_RED_VALUE";
506 		case EGL_TRANSPARENT_GREEN_VALUE:	return "EGL_TRANSPARENT_GREEN_VALUE";
507 		case EGL_TRANSPARENT_BLUE_VALUE:	return "EGL_TRANSPARENT_BLUE_VALUE";
508 		default:							return "<Unknown>";
509 	}
510 }
511 
512 } // anonymous
513 
514 class MultiThreadedConfigTest : public MultiThreadedTest
515 {
516 public:
517 				MultiThreadedConfigTest		(EglTestContext& context, const char* name, const char* description, int getConfigs, int chooseConfigs, int query);
518 	bool		runThread					(TestThread& thread);
519 
520 private:
521 	const int	m_getConfigs;
522 	const int	m_chooseConfigs;
523 	const int	m_query;
524 };
525 
MultiThreadedConfigTest(EglTestContext & context,const char * name,const char * description,int getConfigs,int chooseConfigs,int query)526 MultiThreadedConfigTest::MultiThreadedConfigTest (EglTestContext& context, const char* name, const char* description, int getConfigs, int chooseConfigs, int query)
527 	: MultiThreadedTest (context, name, description, 2, 20000000/*us = 20s*/) // \todo [mika] Set timeout to something relevant to frameworks timeout?
528 	, m_getConfigs		(getConfigs)
529 	, m_chooseConfigs	(chooseConfigs)
530 	, m_query			(query)
531 {
532 }
533 
runThread(TestThread & thread)534 bool MultiThreadedConfigTest::runThread (TestThread& thread)
535 {
536 	const Library&		egl		= getLibrary();
537 	de::Random			rnd		(deInt32Hash(thread.getId() + 10435));
538 	vector<EGLConfig>	configs;
539 
540 	barrier();
541 
542 	for (int getConfigsNdx = 0; getConfigsNdx < m_getConfigs; getConfigsNdx++)
543 	{
544 		EGLint configCount;
545 
546 		// Get number of configs
547 		{
548 			EGLBoolean result;
549 
550 			result = egl.getConfigs(m_display, NULL, 0, &configCount);
551 			thread.getLog() << ThreadLog::BeginMessage << result << " = eglGetConfigs(" << m_display << ", NULL, 0, " << configCount << ")" <<  ThreadLog::EndMessage;
552 			EGLU_CHECK_MSG(egl, "eglGetConfigs()");
553 
554 			if (!result)
555 				return false;
556 		}
557 
558 		configs.resize(configs.size() + configCount);
559 
560 		// Get configs
561 		if (configCount != 0)
562 		{
563 			EGLBoolean result;
564 
565 			result = egl.getConfigs(m_display, &(configs[configs.size() - configCount]), configCount, &configCount);
566 			thread.getLog() << ThreadLog::BeginMessage << result << " = eglGetConfigs(" << m_display << ", &configs' " << configCount << ", " << configCount << ")" <<  ThreadLog::EndMessage;
567 			EGLU_CHECK_MSG(egl, "eglGetConfigs()");
568 
569 			if (!result)
570 				return false;
571 		}
572 
573 		// Pop configs to stop config list growing
574 		if (configs.size() > 40)
575 		{
576 			configs.erase(configs.begin() + 40, configs.end());
577 		}
578 		else
579 		{
580 			const int popCount = rnd.getInt(0, (int)(configs.size()-2));
581 
582 			configs.erase(configs.begin() + (configs.size() - popCount), configs.end());
583 		}
584 	}
585 
586 	for (int chooseConfigsNdx = 0; chooseConfigsNdx < m_chooseConfigs; chooseConfigsNdx++)
587 	{
588 		EGLint configCount;
589 
590 		static const EGLint attribList[] = {
591 			EGL_NONE
592 		};
593 
594 		// Get number of configs
595 		{
596 			EGLBoolean result;
597 
598 			result = egl.chooseConfig(m_display, attribList, NULL, 0, &configCount);
599 			thread.getLog() << ThreadLog::BeginMessage << result << " = eglChooseConfig(" << m_display << ", { EGL_NONE }, NULL, 0, " << configCount << ")" <<  ThreadLog::EndMessage;
600 			EGLU_CHECK_MSG(egl, "eglChooseConfig()");
601 
602 			if (!result)
603 				return false;
604 		}
605 
606 		configs.resize(configs.size() + configCount);
607 
608 		// Get configs
609 		if (configCount != 0)
610 		{
611 			EGLBoolean result;
612 
613 			result = egl.chooseConfig(m_display, attribList, &(configs[configs.size() - configCount]), configCount, &configCount);
614 			thread.getLog() << ThreadLog::BeginMessage << result << " = eglChooseConfig(" << m_display << ", { EGL_NONE }, &configs, " << configCount << ", " << configCount << ")" <<  ThreadLog::EndMessage;
615 			EGLU_CHECK_MSG(egl, "eglChooseConfig()");
616 
617 			if (!result)
618 				return false;
619 		}
620 
621 		// Pop configs to stop config list growing
622 		if (configs.size() > 40)
623 		{
624 			configs.erase(configs.begin() + 40, configs.end());
625 		}
626 		else
627 		{
628 			const int popCount = rnd.getInt(0, (int)(configs.size()-2));
629 
630 			configs.erase(configs.begin() + (configs.size() - popCount), configs.end());
631 		}
632 	}
633 
634 	{
635 		// Perform queries on configs
636 		static const EGLint attributes[] =
637 		{
638 			EGL_BUFFER_SIZE,
639 			EGL_RED_SIZE,
640 			EGL_GREEN_SIZE,
641 			EGL_BLUE_SIZE,
642 			EGL_LUMINANCE_SIZE,
643 			EGL_ALPHA_SIZE,
644 			EGL_ALPHA_MASK_SIZE,
645 			EGL_BIND_TO_TEXTURE_RGB,
646 			EGL_BIND_TO_TEXTURE_RGBA,
647 			EGL_COLOR_BUFFER_TYPE,
648 			EGL_CONFIG_CAVEAT,
649 			EGL_CONFIG_ID,
650 			EGL_CONFORMANT,
651 			EGL_DEPTH_SIZE,
652 			EGL_LEVEL,
653 			EGL_MAX_PBUFFER_WIDTH,
654 			EGL_MAX_PBUFFER_HEIGHT,
655 			EGL_MAX_PBUFFER_PIXELS,
656 			EGL_MAX_SWAP_INTERVAL,
657 			EGL_MIN_SWAP_INTERVAL,
658 			EGL_NATIVE_RENDERABLE,
659 			EGL_NATIVE_VISUAL_ID,
660 			EGL_NATIVE_VISUAL_TYPE,
661 			EGL_RENDERABLE_TYPE,
662 			EGL_SAMPLE_BUFFERS,
663 			EGL_SAMPLES,
664 			EGL_STENCIL_SIZE,
665 			EGL_SURFACE_TYPE,
666 			EGL_TRANSPARENT_TYPE,
667 			EGL_TRANSPARENT_RED_VALUE,
668 			EGL_TRANSPARENT_GREEN_VALUE,
669 			EGL_TRANSPARENT_BLUE_VALUE
670 		};
671 
672 		for (int queryNdx = 0; queryNdx < m_query; queryNdx++)
673 		{
674 			const EGLint	attribute	= attributes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(attributes)-1)];
675 			EGLConfig		config		= configs[rnd.getInt(0, (int)(configs.size()-1))];
676 			EGLint			value;
677 			EGLBoolean		result;
678 
679 			result = egl.getConfigAttrib(m_display, config, attribute, &value);
680 			thread.getLog() << ThreadLog::BeginMessage << result << " = eglGetConfigAttrib(" << m_display << ", " << config << ", " << configAttributeToString(attribute) << ", " << value << ")" <<  ThreadLog::EndMessage;
681 			EGLU_CHECK_MSG(egl, "eglGetConfigAttrib()");
682 
683 			if (!result)
684 				return false;
685 		}
686 	}
687 
688 	return true;
689 }
690 
691 class MultiThreadedObjectTest : public MultiThreadedTest
692 {
693 public:
694 	enum Type
695 	{
696 		TYPE_PBUFFER			= (1<<0),
697 		TYPE_PIXMAP				= (1<<1),
698 		TYPE_WINDOW				= (1<<2),
699 		TYPE_SINGLE_WINDOW		= (1<<3),
700 		TYPE_CONTEXT			= (1<<4)
701 	};
702 
703 					MultiThreadedObjectTest			(EglTestContext& context, const char* name, const char* description, deUint32 types);
704 					~MultiThreadedObjectTest		(void);
705 
706 	virtual void	deinit							(void);
707 
708 	bool			runThread						(TestThread& thread);
709 
710 	void			createDestroyObjects			(TestThread& thread, int count);
711 	void			pushObjectsToShared				(TestThread& thread);
712 	void			pullObjectsFromShared			(TestThread& thread, int pbufferCount, int pixmapCount, int windowCount, int contextCount);
713 	void			querySetSharedObjects			(TestThread& thread, int count);
714 	void			destroyObjects					(TestThread& thread);
715 
716 private:
717 	EGLConfig			m_config;
718 	de::Random			m_rnd0;
719 	de::Random			m_rnd1;
720 	Type				m_types;
721 
722 	volatile deUint32	m_hasWindow;
723 
724 	vector<pair<eglu::NativePixmap*, EGLSurface> >	m_sharedNativePixmaps;
725 	vector<pair<eglu::NativePixmap*, EGLSurface> >	m_nativePixmaps0;
726 	vector<pair<eglu::NativePixmap*, EGLSurface> >	m_nativePixmaps1;
727 
728 	vector<pair<eglu::NativeWindow*, EGLSurface> >	m_sharedNativeWindows;
729 	vector<pair<eglu::NativeWindow*, EGLSurface> >	m_nativeWindows0;
730 	vector<pair<eglu::NativeWindow*, EGLSurface> >	m_nativeWindows1;
731 
732 	vector<EGLSurface>								m_sharedPbuffers;
733 	vector<EGLSurface>								m_pbuffers0;
734 	vector<EGLSurface>								m_pbuffers1;
735 
736 	vector<EGLContext>								m_sharedContexts;
737 	vector<EGLContext>								m_contexts0;
738 	vector<EGLContext>								m_contexts1;
739 };
740 
MultiThreadedObjectTest(EglTestContext & context,const char * name,const char * description,deUint32 type)741 MultiThreadedObjectTest::MultiThreadedObjectTest (EglTestContext& context, const char* name, const char* description, deUint32 type)
742 	: MultiThreadedTest (context, name, description, 2, 20000000/*us = 20s*/) // \todo [mika] Set timeout to something relevant to frameworks timeout?
743 	, m_config			(DE_NULL)
744 	, m_rnd0			(58204327)
745 	, m_rnd1			(230983)
746 	, m_types			((Type)type)
747 	, m_hasWindow		(0)
748 {
749 }
750 
~MultiThreadedObjectTest(void)751 MultiThreadedObjectTest::~MultiThreadedObjectTest (void)
752 {
753 	deinit();
754 }
755 
deinit(void)756 void MultiThreadedObjectTest::deinit (void)
757 {
758 	const Library&		egl		= getLibrary();
759 
760 	// Clear pbuffers
761 	for (int pbufferNdx = 0; pbufferNdx < (int)m_pbuffers0.size(); pbufferNdx++)
762 	{
763 		if (m_pbuffers0[pbufferNdx] != EGL_NO_SURFACE)
764 		{
765 			egl.destroySurface(m_display, m_pbuffers0[pbufferNdx]);
766 			EGLU_CHECK_MSG(egl, "eglDestroySurface()");
767 			m_pbuffers0[pbufferNdx] = EGL_NO_SURFACE;
768 		}
769 	}
770 	m_pbuffers0.clear();
771 
772 	for (int pbufferNdx = 0; pbufferNdx < (int)m_pbuffers1.size(); pbufferNdx++)
773 	{
774 		if (m_pbuffers1[pbufferNdx] != EGL_NO_SURFACE)
775 		{
776 			egl.destroySurface(m_display, m_pbuffers1[pbufferNdx]);
777 			EGLU_CHECK_MSG(egl, "eglDestroySurface()");
778 			m_pbuffers1[pbufferNdx] = EGL_NO_SURFACE;
779 		}
780 	}
781 	m_pbuffers1.clear();
782 
783 	for (int pbufferNdx = 0; pbufferNdx < (int)m_sharedPbuffers.size(); pbufferNdx++)
784 	{
785 		if (m_sharedPbuffers[pbufferNdx] != EGL_NO_SURFACE)
786 		{
787 			egl.destroySurface(m_display, m_sharedPbuffers[pbufferNdx]);
788 			EGLU_CHECK_MSG(egl, "eglDestroySurface()");
789 			m_sharedPbuffers[pbufferNdx] = EGL_NO_SURFACE;
790 		}
791 	}
792 	m_sharedPbuffers.clear();
793 
794 	for (int contextNdx = 0; contextNdx < (int)m_sharedContexts.size(); contextNdx++)
795 	{
796 		if (m_sharedContexts[contextNdx] != EGL_NO_CONTEXT)
797 		{
798 			egl.destroyContext(m_display, m_sharedContexts[contextNdx]);
799 			EGLU_CHECK_MSG(egl, "eglDestroyContext()");
800 			m_sharedContexts[contextNdx] =  EGL_NO_CONTEXT;
801 		}
802 	}
803 	m_sharedContexts.clear();
804 
805 	for (int contextNdx = 0; contextNdx < (int)m_contexts0.size(); contextNdx++)
806 	{
807 		if (m_contexts0[contextNdx] != EGL_NO_CONTEXT)
808 		{
809 			egl.destroyContext(m_display, m_contexts0[contextNdx]);
810 			EGLU_CHECK_MSG(egl, "eglDestroyContext()");
811 			m_contexts0[contextNdx] =  EGL_NO_CONTEXT;
812 		}
813 	}
814 	m_contexts0.clear();
815 
816 	for (int contextNdx = 0; contextNdx < (int)m_contexts1.size(); contextNdx++)
817 	{
818 		if (m_contexts1[contextNdx] != EGL_NO_CONTEXT)
819 		{
820 			egl.destroyContext(m_display, m_contexts1[contextNdx]);
821 			EGLU_CHECK_MSG(egl, "eglDestroyContext()");
822 			m_contexts1[contextNdx] =  EGL_NO_CONTEXT;
823 		}
824 	}
825 	m_contexts1.clear();
826 
827 	// Clear pixmaps
828 	for (int pixmapNdx = 0; pixmapNdx < (int)m_nativePixmaps0.size(); pixmapNdx++)
829 	{
830 		if (m_nativePixmaps0[pixmapNdx].second != EGL_NO_SURFACE)
831 			EGLU_CHECK_CALL(egl, destroySurface(m_display, m_nativePixmaps0[pixmapNdx].second));
832 
833 		m_nativePixmaps0[pixmapNdx].second = EGL_NO_SURFACE;
834 		delete m_nativePixmaps0[pixmapNdx].first;
835 		m_nativePixmaps0[pixmapNdx].first = NULL;
836 	}
837 	m_nativePixmaps0.clear();
838 
839 	for (int pixmapNdx = 0; pixmapNdx < (int)m_nativePixmaps1.size(); pixmapNdx++)
840 	{
841 		if (m_nativePixmaps1[pixmapNdx].second != EGL_NO_SURFACE)
842 			EGLU_CHECK_CALL(egl, destroySurface(m_display, m_nativePixmaps1[pixmapNdx].second));
843 
844 		m_nativePixmaps1[pixmapNdx].second = EGL_NO_SURFACE;
845 		delete m_nativePixmaps1[pixmapNdx].first;
846 		m_nativePixmaps1[pixmapNdx].first = NULL;
847 	}
848 	m_nativePixmaps1.clear();
849 
850 	for (int pixmapNdx = 0; pixmapNdx < (int)m_sharedNativePixmaps.size(); pixmapNdx++)
851 	{
852 		if (m_sharedNativePixmaps[pixmapNdx].second != EGL_NO_SURFACE)
853 			EGLU_CHECK_CALL(egl, destroySurface(m_display, m_sharedNativePixmaps[pixmapNdx].second));
854 
855 		m_sharedNativePixmaps[pixmapNdx].second = EGL_NO_SURFACE;
856 		delete m_sharedNativePixmaps[pixmapNdx].first;
857 		m_sharedNativePixmaps[pixmapNdx].first = NULL;
858 	}
859 	m_sharedNativePixmaps.clear();
860 
861 	// Clear windows
862 	for (int windowNdx = 0; windowNdx < (int)m_nativeWindows1.size(); windowNdx++)
863 	{
864 		if (m_nativeWindows1[windowNdx].second != EGL_NO_SURFACE)
865 			EGLU_CHECK_CALL(egl, destroySurface(m_display, m_nativeWindows1[windowNdx].second));
866 
867 		m_nativeWindows1[windowNdx].second = EGL_NO_SURFACE;
868 		delete m_nativeWindows1[windowNdx].first;
869 		m_nativeWindows1[windowNdx].first = NULL;
870 	}
871 	m_nativeWindows1.clear();
872 
873 	for (int windowNdx = 0; windowNdx < (int)m_nativeWindows0.size(); windowNdx++)
874 	{
875 		if (m_nativeWindows0[windowNdx].second != EGL_NO_SURFACE)
876 			EGLU_CHECK_CALL(egl, destroySurface(m_display, m_nativeWindows0[windowNdx].second));
877 
878 		m_nativeWindows0[windowNdx].second = EGL_NO_SURFACE;
879 		delete m_nativeWindows0[windowNdx].first;
880 		m_nativeWindows0[windowNdx].first = NULL;
881 	}
882 	m_nativeWindows0.clear();
883 
884 	for (int windowNdx = 0; windowNdx < (int)m_sharedNativeWindows.size(); windowNdx++)
885 	{
886 		if (m_sharedNativeWindows[windowNdx].second != EGL_NO_SURFACE)
887 			EGLU_CHECK_CALL(egl, destroySurface(m_display, m_sharedNativeWindows[windowNdx].second));
888 
889 		m_sharedNativeWindows[windowNdx].second = EGL_NO_SURFACE;
890 		delete m_sharedNativeWindows[windowNdx].first;
891 		m_sharedNativeWindows[windowNdx].first = NULL;
892 	}
893 	m_sharedNativeWindows.clear();
894 
895 	MultiThreadedTest::deinit();
896 }
897 
runThread(TestThread & thread)898 bool MultiThreadedObjectTest::runThread (TestThread& thread)
899 {
900 	const Library&		egl		= getLibrary();
901 
902 	if (thread.getId() == 0)
903 	{
904 		EGLint surfaceTypes = 0;
905 
906 		if ((m_types & TYPE_WINDOW) != 0)
907 			surfaceTypes |= EGL_WINDOW_BIT;
908 
909 		if ((m_types & TYPE_PBUFFER) != 0)
910 			surfaceTypes |= EGL_PBUFFER_BIT;
911 
912 		if ((m_types & TYPE_PIXMAP) != 0)
913 			surfaceTypes |= EGL_PIXMAP_BIT;
914 
915 		EGLint configCount;
916 		EGLint attribList[] =
917 		{
918 			EGL_SURFACE_TYPE, surfaceTypes,
919 			EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
920 			EGL_NONE
921 		};
922 
923 		EGLU_CHECK_CALL(egl, chooseConfig(m_display, attribList, &m_config, 1, &configCount));
924 
925 		if (configCount == 0)
926 			TCU_THROW(NotSupportedError, "No usable config found");
927 	}
928 
929 	barrier();
930 
931 	// Create / Destroy Objects
932 	if ((m_types & TYPE_SINGLE_WINDOW) != 0 && (m_types & TYPE_PBUFFER) == 0 && (m_types & TYPE_PIXMAP) == 0 && (m_types & TYPE_CONTEXT) == 0)
933 	{
934 		if (thread.getId() == 0)
935 			createDestroyObjects(thread, 1);
936 	}
937 	else
938 		createDestroyObjects(thread, 100);
939 
940 	// Push first threads objects to shared
941 	if (thread.getId() == 0)
942 		pushObjectsToShared(thread);
943 
944 	barrier();
945 
946 	// Push second threads objects to shared
947 	if (thread.getId() == 1)
948 		pushObjectsToShared(thread);
949 
950 	barrier();
951 
952 	// Make queries from shared surfaces
953 	querySetSharedObjects(thread, 100);
954 
955 	barrier();
956 
957 	// Pull surfaces for first thread from shared surfaces
958 	if (thread.getId() == 0)
959 		pullObjectsFromShared(thread, (int)(m_sharedPbuffers.size()/2), (int)(m_sharedNativePixmaps.size()/2), (int)(m_sharedNativeWindows.size()/2), (int)(m_sharedContexts.size()/2));
960 
961 	barrier();
962 
963 	// Pull surfaces for second thread from shared surfaces
964 	if (thread.getId() == 1)
965 		pullObjectsFromShared(thread, (int)m_sharedPbuffers.size(), (int)m_sharedNativePixmaps.size(), (int)m_sharedNativeWindows.size(), (int)m_sharedContexts.size());
966 
967 	barrier();
968 
969 	// Create / Destroy Objects
970 	if ((m_types & TYPE_SINGLE_WINDOW) == 0)
971 		createDestroyObjects(thread, 100);
972 
973 	// Destroy surfaces
974 	destroyObjects(thread);
975 
976 	return true;
977 }
978 
createDestroyObjects(TestThread & thread,int count)979 void MultiThreadedObjectTest::createDestroyObjects (TestThread& thread, int count)
980 {
981 	const Library&									egl			= getLibrary();
982 	de::Random&										rnd			= (thread.getId() == 0 ? m_rnd0 : m_rnd1);
983 	vector<EGLSurface>&								pbuffers	= (thread.getId() == 0 ? m_pbuffers0 : m_pbuffers1);
984 	vector<pair<eglu::NativeWindow*, EGLSurface> >&	windows		= (thread.getId() == 0 ? m_nativeWindows0 : m_nativeWindows1);
985 	vector<pair<eglu::NativePixmap*, EGLSurface> >&	pixmaps		= (thread.getId() == 0 ? m_nativePixmaps0 : m_nativePixmaps1);
986 	vector<EGLContext>&								contexts	= (thread.getId() == 0 ? m_contexts0 : m_contexts1);
987 	set<Type>										objectTypes;
988 
989 	if ((m_types & TYPE_PBUFFER) != 0)
990 		objectTypes.insert(TYPE_PBUFFER);
991 
992 	if ((m_types & TYPE_PIXMAP) != 0)
993 		objectTypes.insert(TYPE_PIXMAP);
994 
995 	if ((m_types & TYPE_WINDOW) != 0)
996 		objectTypes.insert(TYPE_WINDOW);
997 
998 	if ((m_types & TYPE_CONTEXT) != 0)
999 		objectTypes.insert(TYPE_CONTEXT);
1000 
1001 	for (int createDestroyNdx = 0; createDestroyNdx < count; createDestroyNdx++)
1002 	{
1003 		bool create;
1004 		Type type;
1005 
1006 		if (pbuffers.size() > 5 && ((m_types & TYPE_PBUFFER) != 0))
1007 		{
1008 			create	= false;
1009 			type	= TYPE_PBUFFER;
1010 		}
1011 		else if (windows.size() > 5 && ((m_types & TYPE_WINDOW) != 0))
1012 		{
1013 			create	= false;
1014 			type	= TYPE_WINDOW;
1015 		}
1016 		else if (pixmaps.size() > 5 && ((m_types & TYPE_PIXMAP) != 0))
1017 		{
1018 			create	= false;
1019 			type	= TYPE_PIXMAP;
1020 		}
1021 		else if (contexts.size() > 5 && ((m_types & TYPE_CONTEXT) != 0))
1022 		{
1023 			create	= false;
1024 			type	= TYPE_CONTEXT;
1025 		}
1026 		else if (pbuffers.size() < 3 && ((m_types & TYPE_PBUFFER) != 0))
1027 		{
1028 			create	= true;
1029 			type	= TYPE_PBUFFER;
1030 		}
1031 		else if (pixmaps.size() < 3 && ((m_types & TYPE_PIXMAP) != 0))
1032 		{
1033 			create	= true;
1034 			type	= TYPE_PIXMAP;
1035 		}
1036 		else if (contexts.size() < 3 && ((m_types & TYPE_CONTEXT) != 0))
1037 		{
1038 			create	= true;
1039 			type	= TYPE_CONTEXT;
1040 		}
1041 		else if (windows.size() < 3 && ((m_types & TYPE_WINDOW) != 0) && ((m_types & TYPE_SINGLE_WINDOW) == 0))
1042 		{
1043 			create	= true;
1044 			type	= TYPE_WINDOW;
1045 		}
1046 		else if (windows.empty() && (m_hasWindow == 0) && ((m_types & TYPE_WINDOW) != 0) && ((m_types & TYPE_SINGLE_WINDOW) != 0))
1047 		{
1048 			create	= true;
1049 			type	= TYPE_WINDOW;
1050 		}
1051 		else
1052 		{
1053 			create = rnd.getBool();
1054 
1055 			if (!create && windows.empty())
1056 				objectTypes.erase(TYPE_WINDOW);
1057 
1058 			type = rnd.choose<Type>(objectTypes.begin(), objectTypes.end());
1059 		}
1060 
1061 		if (create)
1062 		{
1063 			switch (type)
1064 			{
1065 				case TYPE_PBUFFER:
1066 				{
1067 					EGLSurface surface;
1068 
1069 					const EGLint attributes[] =
1070 					{
1071 						EGL_WIDTH,	64,
1072 						EGL_HEIGHT,	64,
1073 
1074 						EGL_NONE
1075 					};
1076 
1077 					surface = egl.createPbufferSurface(m_display, m_config, attributes);
1078 					thread.getLog() << ThreadLog::BeginMessage << surface << " = eglCreatePbufferSurface(" << m_display << ", " << m_config << ", { EGL_WIDTH, 64, EGL_HEIGHT, 64, EGL_NONE })" << ThreadLog::EndMessage;
1079 					EGLU_CHECK_MSG(egl, "eglCreatePbufferSurface()");
1080 
1081 					pbuffers.push_back(surface);
1082 
1083 					break;
1084 				}
1085 
1086 				case TYPE_WINDOW:
1087 				{
1088 					const eglu::NativeWindowFactory&	windowFactory	= eglu::selectNativeWindowFactory(m_eglTestCtx.getNativeDisplayFactory(), m_testCtx.getCommandLine());
1089 
1090 					if ((m_types & TYPE_SINGLE_WINDOW) != 0)
1091 					{
1092 						if (deAtomicCompareExchange32(&m_hasWindow, 0, 1) == 0)
1093 						{
1094 							eglu::NativeWindow* window	= DE_NULL;
1095 							EGLSurface			surface = EGL_NO_SURFACE;
1096 
1097 							try
1098 							{
1099 								window = windowFactory.createWindow(&m_eglTestCtx.getNativeDisplay(), m_display, m_config, DE_NULL, eglu::WindowParams(64, 64, eglu::parseWindowVisibility(m_testCtx.getCommandLine())));
1100 								surface = eglu::createWindowSurface(m_eglTestCtx.getNativeDisplay(), *window, m_display, m_config, DE_NULL);
1101 
1102 								thread.getLog() << ThreadLog::BeginMessage << surface << " = eglCreateWindowSurface()" << ThreadLog::EndMessage;
1103 								windows.push_back(std::make_pair(window, surface));
1104 							}
1105 							catch (const std::exception&)
1106 							{
1107 								if (surface != EGL_NO_SURFACE)
1108 									EGLU_CHECK_CALL(egl, destroySurface(m_display, surface));
1109 								delete window;
1110 								m_hasWindow = 0;
1111 								throw;
1112 							}
1113 						}
1114 						else
1115 						{
1116 							createDestroyNdx--;
1117 						}
1118 					}
1119 					else
1120 					{
1121 						eglu::NativeWindow* window	= DE_NULL;
1122 						EGLSurface			surface = EGL_NO_SURFACE;
1123 
1124 						try
1125 						{
1126 							window	= windowFactory.createWindow(&m_eglTestCtx.getNativeDisplay(), m_display, m_config, DE_NULL, eglu::WindowParams(64, 64, eglu::parseWindowVisibility(m_testCtx.getCommandLine())));
1127 							surface	= eglu::createWindowSurface(m_eglTestCtx.getNativeDisplay(), *window, m_display, m_config, DE_NULL);
1128 
1129 							thread.getLog() << ThreadLog::BeginMessage << surface << " = eglCreateWindowSurface()" << ThreadLog::EndMessage;
1130 							windows.push_back(std::make_pair(window, surface));
1131 						}
1132 						catch (const std::exception&)
1133 						{
1134 							if (surface != EGL_NO_SURFACE)
1135 								EGLU_CHECK_CALL(egl, destroySurface(m_display, surface));
1136 							delete window;
1137 							throw;
1138 						}
1139 					}
1140 					break;
1141 				}
1142 
1143 				case TYPE_PIXMAP:
1144 				{
1145 					const eglu::NativePixmapFactory&	pixmapFactory	= eglu::selectNativePixmapFactory(m_eglTestCtx.getNativeDisplayFactory(), m_testCtx.getCommandLine());
1146 					eglu::NativePixmap*					pixmap			= DE_NULL;
1147 					EGLSurface							surface			= EGL_NO_SURFACE;
1148 
1149 					try
1150 					{
1151 						pixmap	= pixmapFactory.createPixmap(&m_eglTestCtx.getNativeDisplay(), m_display, m_config, DE_NULL, 64, 64);
1152 						surface	= eglu::createPixmapSurface(m_eglTestCtx.getNativeDisplay(), *pixmap, m_display, m_config, DE_NULL);
1153 
1154 						thread.getLog() << ThreadLog::BeginMessage << surface << " = eglCreatePixmapSurface()" << ThreadLog::EndMessage;
1155 						pixmaps.push_back(std::make_pair(pixmap, surface));
1156 					}
1157 					catch (const std::exception&)
1158 					{
1159 						if (surface != EGL_NO_SURFACE)
1160 							EGLU_CHECK_CALL(egl, destroySurface(m_display, surface));
1161 						delete pixmap;
1162 						throw;
1163 					}
1164 					break;
1165 				}
1166 
1167 				case TYPE_CONTEXT:
1168 				{
1169 					EGLContext context;
1170 
1171 					EGLU_CHECK_CALL(egl, bindAPI(EGL_OPENGL_ES_API));
1172 					thread.getLog() << ThreadLog::BeginMessage << "eglBindAPI(EGL_OPENGL_ES_API)" << ThreadLog::EndMessage;
1173 
1174 					const EGLint attributes[] =
1175 					{
1176 						EGL_CONTEXT_CLIENT_VERSION, 2,
1177 						EGL_NONE
1178 					};
1179 
1180 					context = egl.createContext(m_display, m_config, EGL_NO_CONTEXT, attributes);
1181 					thread.getLog() << ThreadLog::BeginMessage << context << " = eglCreateContext(" << m_display << ", " << m_config << ", EGL_NO_CONTEXT, { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE })" << ThreadLog::EndMessage;
1182 					EGLU_CHECK_MSG(egl, "eglCreateContext()");
1183 					contexts.push_back(context);
1184 					break;
1185 				}
1186 
1187 				default:
1188 					DE_ASSERT(false);
1189 			};
1190 		}
1191 		else
1192 		{
1193 			switch (type)
1194 			{
1195 				case TYPE_PBUFFER:
1196 				{
1197 					const int pbufferNdx = rnd.getInt(0, (int)(pbuffers.size()-1));
1198 					EGLBoolean result;
1199 
1200 					result = egl.destroySurface(m_display, pbuffers[pbufferNdx]);
1201 					thread.getLog() << ThreadLog::BeginMessage << result << " = eglDestroySurface(" << m_display << ", " << pbuffers[pbufferNdx] << ")" << ThreadLog::EndMessage;
1202 					EGLU_CHECK_MSG(egl, "eglDestroySurface()");
1203 
1204 					pbuffers.erase(pbuffers.begin() + pbufferNdx);
1205 
1206 					break;
1207 				}
1208 
1209 				case TYPE_WINDOW:
1210 				{
1211 					const int windowNdx = rnd.getInt(0, (int)(windows.size()-1));
1212 
1213 					thread.getLog() << ThreadLog::BeginMessage << "eglDestroySurface(" << m_display << ", " << windows[windowNdx].second << ")" << ThreadLog::EndMessage;
1214 
1215 					EGLU_CHECK_CALL(egl, destroySurface(m_display, windows[windowNdx].second));
1216 					windows[windowNdx].second = EGL_NO_SURFACE;
1217 					delete windows[windowNdx].first;
1218 					windows[windowNdx].first = DE_NULL;
1219 					windows.erase(windows.begin() + windowNdx);
1220 
1221 					if ((m_types & TYPE_SINGLE_WINDOW) != 0)
1222 						m_hasWindow = 0;
1223 
1224 					break;
1225 				}
1226 
1227 				case TYPE_PIXMAP:
1228 				{
1229 					const int pixmapNdx = rnd.getInt(0, (int)(pixmaps.size()-1));
1230 
1231 					thread.getLog() << ThreadLog::BeginMessage << "eglDestroySurface(" << m_display << ", " << pixmaps[pixmapNdx].second << ")" << ThreadLog::EndMessage;
1232 					EGLU_CHECK_CALL(egl, destroySurface(m_display, pixmaps[pixmapNdx].second));
1233 					pixmaps[pixmapNdx].second = EGL_NO_SURFACE;
1234 					delete pixmaps[pixmapNdx].first;
1235 					pixmaps[pixmapNdx].first = DE_NULL;
1236 					pixmaps.erase(pixmaps.begin() + pixmapNdx);
1237 
1238 					break;
1239 				}
1240 
1241 				case TYPE_CONTEXT:
1242 				{
1243 					const int contextNdx = rnd.getInt(0, (int)(contexts.size()-1));
1244 
1245 					EGLU_CHECK_CALL(egl, destroyContext(m_display, contexts[contextNdx]));
1246 					thread.getLog() << ThreadLog::BeginMessage << "eglDestroyContext(" << m_display << ", " << contexts[contextNdx]  << ")" << ThreadLog::EndMessage;
1247 					contexts.erase(contexts.begin() + contextNdx);
1248 
1249 					break;
1250 				}
1251 
1252 				default:
1253 					DE_ASSERT(false);
1254 			}
1255 
1256 		}
1257 	}
1258 }
1259 
pushObjectsToShared(TestThread & thread)1260 void MultiThreadedObjectTest::pushObjectsToShared (TestThread& thread)
1261 {
1262 	vector<EGLSurface>&									pbuffers	= (thread.getId() == 0 ? m_pbuffers0 : m_pbuffers1);
1263 	vector<pair<eglu::NativeWindow*, EGLSurface> >&		windows		= (thread.getId() == 0 ? m_nativeWindows0 : m_nativeWindows1);
1264 	vector<pair<eglu::NativePixmap*, EGLSurface> >&		pixmaps		= (thread.getId() == 0 ? m_nativePixmaps0 : m_nativePixmaps1);
1265 	vector<EGLContext>&									contexts	= (thread.getId() == 0 ? m_contexts0 : m_contexts1);
1266 
1267 	for (int pbufferNdx = 0; pbufferNdx < (int)pbuffers.size(); pbufferNdx++)
1268 		m_sharedPbuffers.push_back(pbuffers[pbufferNdx]);
1269 
1270 	pbuffers.clear();
1271 
1272 	for (int windowNdx = 0; windowNdx < (int)windows.size(); windowNdx++)
1273 		m_sharedNativeWindows.push_back(windows[windowNdx]);
1274 
1275 	windows.clear();
1276 
1277 	for (int pixmapNdx = 0; pixmapNdx < (int)pixmaps.size(); pixmapNdx++)
1278 		m_sharedNativePixmaps.push_back(pixmaps[pixmapNdx]);
1279 
1280 	pixmaps.clear();
1281 
1282 	for (int contextNdx = 0; contextNdx < (int)contexts.size(); contextNdx++)
1283 		m_sharedContexts.push_back(contexts[contextNdx]);
1284 
1285 	contexts.clear();
1286 }
1287 
pullObjectsFromShared(TestThread & thread,int pbufferCount,int pixmapCount,int windowCount,int contextCount)1288 void MultiThreadedObjectTest::pullObjectsFromShared (TestThread& thread, int pbufferCount, int pixmapCount, int windowCount, int contextCount)
1289 {
1290 	de::Random&											rnd			= (thread.getId() == 0 ? m_rnd0 : m_rnd1);
1291 	vector<EGLSurface>&									pbuffers	= (thread.getId() == 0 ? m_pbuffers0 : m_pbuffers1);
1292 	vector<pair<eglu::NativeWindow*, EGLSurface> >&		windows		= (thread.getId() == 0 ? m_nativeWindows0 : m_nativeWindows1);
1293 	vector<pair<eglu::NativePixmap*, EGLSurface> >&		pixmaps		= (thread.getId() == 0 ? m_nativePixmaps0 : m_nativePixmaps1);
1294 	vector<EGLContext>&									contexts	= (thread.getId() == 0 ? m_contexts0 : m_contexts1);
1295 
1296 	for (int pbufferNdx = 0; pbufferNdx < pbufferCount; pbufferNdx++)
1297 	{
1298 		const int ndx = rnd.getInt(0, (int)(m_sharedPbuffers.size()-1));
1299 
1300 		pbuffers.push_back(m_sharedPbuffers[ndx]);
1301 		m_sharedPbuffers.erase(m_sharedPbuffers.begin() + ndx);
1302 	}
1303 
1304 	for (int pixmapNdx = 0; pixmapNdx < pixmapCount; pixmapNdx++)
1305 	{
1306 		const int ndx = rnd.getInt(0, (int)(m_sharedNativePixmaps.size()-1));
1307 
1308 		pixmaps.push_back(m_sharedNativePixmaps[ndx]);
1309 		m_sharedNativePixmaps.erase(m_sharedNativePixmaps.begin() + ndx);
1310 	}
1311 
1312 	for (int windowNdx = 0; windowNdx < windowCount; windowNdx++)
1313 	{
1314 		const int ndx = rnd.getInt(0, (int)(m_sharedNativeWindows.size()-1));
1315 
1316 		windows.push_back(m_sharedNativeWindows[ndx]);
1317 		m_sharedNativeWindows.erase(m_sharedNativeWindows.begin() + ndx);
1318 	}
1319 
1320 	for (int contextNdx = 0; contextNdx < contextCount; contextNdx++)
1321 	{
1322 		const int ndx = rnd.getInt(0, (int)(m_sharedContexts.size()-1));
1323 
1324 		contexts.push_back(m_sharedContexts[ndx]);
1325 		m_sharedContexts.erase(m_sharedContexts.begin() + ndx);
1326 	}
1327 }
1328 
querySetSharedObjects(TestThread & thread,int count)1329 void MultiThreadedObjectTest::querySetSharedObjects (TestThread& thread, int count)
1330 {
1331 	const Library&		egl		= getLibrary();
1332 	de::Random&			rnd		= (thread.getId() == 0 ? m_rnd0 : m_rnd1);
1333 	vector<Type>		objectTypes;
1334 
1335 	if ((m_types & TYPE_PBUFFER) != 0)
1336 		objectTypes.push_back(TYPE_PBUFFER);
1337 
1338 	if ((m_types & TYPE_PIXMAP) != 0)
1339 		objectTypes.push_back(TYPE_PIXMAP);
1340 
1341 	if (!m_sharedNativeWindows.empty() && (m_types & TYPE_WINDOW) != 0)
1342 		objectTypes.push_back(TYPE_WINDOW);
1343 
1344 	if ((m_types & TYPE_CONTEXT) != 0)
1345 		objectTypes.push_back(TYPE_CONTEXT);
1346 
1347 	for (int queryNdx = 0; queryNdx < count; queryNdx++)
1348 	{
1349 		const Type	type		= rnd.choose<Type>(objectTypes.begin(), objectTypes.end());
1350 		EGLSurface	surface		= EGL_NO_SURFACE;
1351 		EGLContext	context		= EGL_NO_CONTEXT;
1352 
1353 		switch (type)
1354 		{
1355 			case TYPE_PBUFFER:
1356 				surface = m_sharedPbuffers[rnd.getInt(0, (int)(m_sharedPbuffers.size()-1))];
1357 				break;
1358 
1359 			case TYPE_PIXMAP:
1360 				surface = m_sharedNativePixmaps[rnd.getInt(0, (int)(m_sharedNativePixmaps.size()-1))].second;
1361 				break;
1362 
1363 			case TYPE_WINDOW:
1364 				surface = m_sharedNativeWindows[rnd.getInt(0, (int)(m_sharedNativeWindows.size()-1))].second;
1365 				break;
1366 
1367 			case TYPE_CONTEXT:
1368 				context = m_sharedContexts[rnd.getInt(0, (int)(m_sharedContexts.size()-1))];
1369 				break;
1370 
1371 			default:
1372 				DE_ASSERT(false);
1373 		}
1374 
1375 		if (surface != EGL_NO_SURFACE)
1376 		{
1377 			static const EGLint queryAttributes[] =
1378 			{
1379 				EGL_LARGEST_PBUFFER,
1380 				EGL_HEIGHT,
1381 				EGL_WIDTH
1382 			};
1383 
1384 			const EGLint	attribute	= queryAttributes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(queryAttributes) - 1)];
1385 			EGLBoolean		result;
1386 			EGLint			value;
1387 
1388 			result = egl.querySurface(m_display, surface, attribute, &value);
1389 			thread.getLog() << ThreadLog::BeginMessage << result << " = eglQuerySurface(" << m_display << ", " << surface << ", " << attribute << ", " << value << ")" << ThreadLog::EndMessage;
1390 			EGLU_CHECK_MSG(egl, "eglQuerySurface()");
1391 
1392 		}
1393 		else if (context != EGL_NO_CONTEXT)
1394 		{
1395 			static const EGLint attributes[] =
1396 			{
1397 				EGL_CONFIG_ID,
1398 				EGL_CONTEXT_CLIENT_TYPE,
1399 				EGL_CONTEXT_CLIENT_VERSION,
1400 				EGL_RENDER_BUFFER
1401 			};
1402 
1403 			const EGLint	attribute = attributes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(attributes)-1)];
1404 			EGLint			value;
1405 			EGLBoolean		result;
1406 
1407 			result = egl.queryContext(m_display, context, attribute, &value);
1408 			thread.getLog() << ThreadLog::BeginMessage << result << " = eglQueryContext(" << m_display << ", " << context << ", " << attribute << ", " << value << ")" << ThreadLog::EndMessage;
1409 			EGLU_CHECK_MSG(egl, "eglQueryContext()");
1410 
1411 		}
1412 		else
1413 			DE_ASSERT(false);
1414 	}
1415 }
1416 
destroyObjects(TestThread & thread)1417 void MultiThreadedObjectTest::destroyObjects (TestThread& thread)
1418 {
1419 	const Library&										egl			= getLibrary();
1420 	vector<EGLSurface>&									pbuffers	= (thread.getId() == 0 ? m_pbuffers0 : m_pbuffers1);
1421 	vector<pair<eglu::NativeWindow*, EGLSurface> >&		windows		= (thread.getId() == 0 ? m_nativeWindows0 : m_nativeWindows1);
1422 	vector<pair<eglu::NativePixmap*, EGLSurface> >&		pixmaps		= (thread.getId() == 0 ? m_nativePixmaps0 : m_nativePixmaps1);
1423 	vector<EGLContext>&									contexts	= (thread.getId() == 0 ? m_contexts0 : m_contexts1);
1424 
1425 	for (int pbufferNdx = 0; pbufferNdx < (int)pbuffers.size(); pbufferNdx++)
1426 	{
1427 		if (pbuffers[pbufferNdx] != EGL_NO_SURFACE)
1428 		{
1429 			// Destroy EGLSurface
1430 			EGLBoolean result;
1431 
1432 			result = egl.destroySurface(m_display, pbuffers[pbufferNdx]);
1433 			thread.getLog() << ThreadLog::BeginMessage << result << " = eglDestroySurface(" << m_display << ", " << pbuffers[pbufferNdx] << ")" << ThreadLog::EndMessage;
1434 			EGLU_CHECK_MSG(egl, "eglDestroySurface()");
1435 			pbuffers[pbufferNdx] = EGL_NO_SURFACE;
1436 		}
1437 	}
1438 	pbuffers.clear();
1439 
1440 	for (int windowNdx = 0; windowNdx < (int)windows.size(); windowNdx++)
1441 	{
1442 		if (windows[windowNdx].second != EGL_NO_SURFACE)
1443 		{
1444 			thread.getLog() << ThreadLog::BeginMessage << "eglDestroySurface(" << m_display << ", " << windows[windowNdx].second << ")" << ThreadLog::EndMessage;
1445 			EGLU_CHECK_CALL(egl, destroySurface(m_display, windows[windowNdx].second));
1446 			windows[windowNdx].second = EGL_NO_SURFACE;
1447 		}
1448 
1449 		if (windows[windowNdx].first)
1450 		{
1451 			delete windows[windowNdx].first;
1452 			windows[windowNdx].first = NULL;
1453 		}
1454 	}
1455 	windows.clear();
1456 
1457 	for (int pixmapNdx = 0; pixmapNdx < (int)pixmaps.size(); pixmapNdx++)
1458 	{
1459 		if (pixmaps[pixmapNdx].first != EGL_NO_SURFACE)
1460 		{
1461 			thread.getLog() << ThreadLog::BeginMessage << "eglDestroySurface(" << m_display << ", " << pixmaps[pixmapNdx].second << ")" << ThreadLog::EndMessage;
1462 			EGLU_CHECK_CALL(egl, destroySurface(m_display, pixmaps[pixmapNdx].second));
1463 			pixmaps[pixmapNdx].second = EGL_NO_SURFACE;
1464 		}
1465 
1466 		if (pixmaps[pixmapNdx].first)
1467 		{
1468 			delete pixmaps[pixmapNdx].first;
1469 			pixmaps[pixmapNdx].first = NULL;
1470 		}
1471 	}
1472 	pixmaps.clear();
1473 
1474 	for (int contextNdx = 0; contextNdx < (int)contexts.size(); contextNdx++)
1475 	{
1476 		if (contexts[contextNdx] != EGL_NO_CONTEXT)
1477 		{
1478 			EGLU_CHECK_CALL(egl, destroyContext(m_display, contexts[contextNdx]));
1479 			thread.getLog() << ThreadLog::BeginMessage << "eglDestroyContext(" << m_display << ", " << contexts[contextNdx]  << ")" << ThreadLog::EndMessage;
1480 			contexts[contextNdx] = EGL_NO_CONTEXT;
1481 		}
1482 	}
1483 	contexts.clear();
1484 }
1485 
MultiThreadedTests(EglTestContext & context)1486 MultiThreadedTests::MultiThreadedTests (EglTestContext& context)
1487 	: TestCaseGroup(context, "multithread", "Multithreaded EGL tests")
1488 {
1489 }
1490 
init(void)1491 void MultiThreadedTests::init (void)
1492 {
1493 	// Config tests
1494 	addChild(new MultiThreadedConfigTest(m_eglTestCtx,	"config",	"",	30,	30,	30));
1495 
1496 	// Object tests
1497 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"pbuffer",								"", MultiThreadedObjectTest::TYPE_PBUFFER));
1498 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"pixmap",								"", MultiThreadedObjectTest::TYPE_PIXMAP));
1499 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"window",								"", MultiThreadedObjectTest::TYPE_WINDOW));
1500 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"single_window",						"", MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_SINGLE_WINDOW));
1501 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"context",								"", MultiThreadedObjectTest::TYPE_CONTEXT));
1502 
1503 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"pbuffer_pixmap",						"", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_PIXMAP));
1504 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"pbuffer_window",						"", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_WINDOW));
1505 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"pbuffer_single_window",				"", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_SINGLE_WINDOW));
1506 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"pbuffer_context",						"", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_CONTEXT));
1507 
1508 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"pixmap_window",						"", MultiThreadedObjectTest::TYPE_PIXMAP|MultiThreadedObjectTest::TYPE_WINDOW));
1509 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"pixmap_single_window",					"", MultiThreadedObjectTest::TYPE_PIXMAP|MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_SINGLE_WINDOW));
1510 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"pixmap_context",						"", MultiThreadedObjectTest::TYPE_PIXMAP|MultiThreadedObjectTest::TYPE_CONTEXT));
1511 
1512 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"window_context",						"", MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_CONTEXT));
1513 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"single_window_context",				"", MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_SINGLE_WINDOW|MultiThreadedObjectTest::TYPE_CONTEXT));
1514 
1515 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"pbuffer_pixmap_window",				"", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_PIXMAP|MultiThreadedObjectTest::TYPE_WINDOW));
1516 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"pbuffer_pixmap_single_window",			"", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_PIXMAP|MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_SINGLE_WINDOW));
1517 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"pbuffer_pixmap_context",				"", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_PIXMAP|MultiThreadedObjectTest::TYPE_CONTEXT));
1518 
1519 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"pbuffer_window_context",				"", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_CONTEXT));
1520 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"pbuffer_single_window_context",		"", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_SINGLE_WINDOW|MultiThreadedObjectTest::TYPE_CONTEXT));
1521 
1522 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"pixmap_window_context",				"", MultiThreadedObjectTest::TYPE_PIXMAP|MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_CONTEXT));
1523 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"pixmap_single_window_context",			"", MultiThreadedObjectTest::TYPE_PIXMAP|MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_SINGLE_WINDOW|MultiThreadedObjectTest::TYPE_CONTEXT));
1524 
1525 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"pbuffer_pixmap_window_context",		"", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_PIXMAP|MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_CONTEXT));
1526 	addChild(new MultiThreadedObjectTest(m_eglTestCtx,	"pbuffer_pixmap_single_window_context",	"", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_PIXMAP|MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_SINGLE_WINDOW|MultiThreadedObjectTest::TYPE_CONTEXT));
1527 }
1528 
1529 } // egl
1530 } // deqp
1531