xref: /aosp_15_r20/external/deqp/modules/egl/teglMultiThreadTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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