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