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