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