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