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