| /*------------------------------------------------------------------------- |
| * drawElements Quality Program EGL Module |
| * --------------------------------------- |
| * |
| * Copyright 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| *//*! |
| * \file |
| * \brief Multi threaded EGL tests |
| *//*--------------------------------------------------------------------*/ |
| #include "teglMultiThreadTests.hpp" |
| |
| #include "egluNativeWindow.hpp" |
| #include "egluNativePixmap.hpp" |
| #include "egluUtil.hpp" |
| |
| #include "tcuTestLog.hpp" |
| #include "tcuCommandLine.hpp" |
| |
| #include "deRandom.hpp" |
| |
| #include "deThread.hpp" |
| #include "deMutex.hpp" |
| #include "deSemaphore.hpp" |
| |
| #include "deAtomic.h" |
| #include "deClock.h" |
| |
| #include "eglwLibrary.hpp" |
| #include "eglwEnums.hpp" |
| |
| #include <vector> |
| #include <set> |
| #include <string> |
| #include <sstream> |
| |
| using std::vector; |
| using std::string; |
| using std::pair; |
| using std::set; |
| using std::ostringstream; |
| |
| using namespace eglw; |
| |
| namespace deqp |
| { |
| namespace egl |
| { |
| |
| class ThreadLog |
| { |
| public: |
| class BeginMessageToken {}; |
| class EndMessageToken {}; |
| |
| struct Message |
| { |
| Message (deUint64 timeUs_, const char* msg_) : timeUs(timeUs_), msg(msg_) {} |
| |
| deUint64 timeUs; |
| string msg; |
| }; |
| |
| ThreadLog (void) { m_messages.reserve(100); } |
| |
| ThreadLog& operator<< (const BeginMessageToken&) { return *this; } |
| ThreadLog& operator<< (const EndMessageToken&); |
| |
| template<class T> |
| ThreadLog& operator<< (const T& t) { m_message << t; return *this; } |
| const vector<Message>& getMessages (void) const { return m_messages; } |
| |
| static BeginMessageToken BeginMessage; |
| static EndMessageToken EndMessage; |
| |
| private: |
| ostringstream m_message; |
| vector<Message> m_messages; |
| }; |
| |
| ThreadLog& ThreadLog::operator<< (const EndMessageToken&) |
| { |
| m_messages.push_back(Message(deGetMicroseconds(), m_message.str().c_str())); |
| m_message.str(""); |
| return *this; |
| } |
| |
| ThreadLog::BeginMessageToken ThreadLog::BeginMessage; |
| ThreadLog::EndMessageToken ThreadLog::EndMessage; |
| |
| class MultiThreadedTest; |
| |
| class TestThread : public de::Thread |
| { |
| public: |
| enum ThreadStatus |
| { |
| THREADSTATUS_NOT_STARTED = 0, |
| THREADSTATUS_RUNNING, |
| THREADSTATUS_READY, |
| }; |
| |
| TestThread (MultiThreadedTest& test, int id); |
| void run (void); |
| |
| ThreadStatus getStatus (void) const { return m_status; } |
| ThreadLog& getLog (void) { return m_log; } |
| |
| int getId (void) const { return m_id; } |
| |
| void setStatus (ThreadStatus status) { m_status = status; } |
| |
| const Library& getLibrary (void) const; |
| |
| // Test has stopped |
| class TestStop {}; |
| |
| |
| private: |
| MultiThreadedTest& m_test; |
| const int m_id; |
| ThreadStatus m_status; |
| ThreadLog m_log; |
| }; |
| |
| class MultiThreadedTest : public TestCase |
| { |
| public: |
| MultiThreadedTest (EglTestContext& eglTestCtx, const char* name, const char* description, int threadCount, deUint64 timeoutUs); |
| virtual ~MultiThreadedTest (void); |
| |
| void init (void); |
| void deinit (void); |
| |
| virtual bool runThread (TestThread& thread) = 0; |
| virtual IterateResult iterate (void); |
| void execTest (TestThread& thread); |
| |
| const Library& getLibrary (void) const { return m_eglTestCtx.getLibrary(); } |
| |
| protected: |
| void barrier (void); |
| |
| private: |
| int m_threadCount; |
| bool m_initialized; |
| deUint64 m_startTimeUs; |
| const deUint64 m_timeoutUs; |
| bool m_ok; |
| bool m_supported; |
| vector<TestThread*> m_threads; |
| |
| volatile deInt32 m_barrierWaiters; |
| de::Semaphore m_barrierSemaphore1; |
| de::Semaphore m_barrierSemaphore2; |
| |
| protected: |
| EGLDisplay m_display; |
| }; |
| |
| inline const Library& TestThread::getLibrary (void) const |
| { |
| return m_test.getLibrary(); |
| } |
| |
| TestThread::TestThread (MultiThreadedTest& test, int id) |
| : m_test (test) |
| , m_id (id) |
| , m_status (THREADSTATUS_NOT_STARTED) |
| { |
| } |
| |
| void TestThread::run (void) |
| { |
| m_status = THREADSTATUS_RUNNING; |
| |
| try |
| { |
| m_test.execTest(*this); |
| } |
| catch (const TestThread::TestStop&) |
| { |
| getLog() << ThreadLog::BeginMessage << "Thread stopped" << ThreadLog::EndMessage; |
| } |
| catch (const tcu::NotSupportedError& e) |
| { |
| getLog() << ThreadLog::BeginMessage << "Not supported: '" << e.what() << "'" << ThreadLog::EndMessage; |
| } |
| catch (const std::exception& e) |
| { |
| getLog() << ThreadLog::BeginMessage << "Got exception: '" << e.what() << "'" << ThreadLog::EndMessage; |
| } |
| catch (...) |
| { |
| getLog() << ThreadLog::BeginMessage << "Unknown exception" << ThreadLog::EndMessage; |
| } |
| |
| getLibrary().releaseThread(); |
| m_status = THREADSTATUS_READY; |
| } |
| |
| void MultiThreadedTest::execTest (TestThread& thread) |
| { |
| try |
| { |
| if (!runThread(thread)) |
| m_ok = false; |
| } |
| catch (const TestThread::TestStop&) |
| { |
| // Thread exited due to error in other thread |
| throw; |
| } |
| catch (const tcu::NotSupportedError&) |
| { |
| m_supported = false; |
| |
| // Release barriers |
| for (int threadNdx = 0; threadNdx < (int)m_threads.size(); threadNdx++) |
| { |
| m_barrierSemaphore1.increment(); |
| m_barrierSemaphore2.increment(); |
| } |
| |
| throw; |
| } |
| catch(...) |
| { |
| m_ok = false; |
| |
| // Release barriers |
| for (int threadNdx = 0; threadNdx < (int)m_threads.size(); threadNdx++) |
| { |
| m_barrierSemaphore1.increment(); |
| m_barrierSemaphore2.increment(); |
| } |
| |
| throw; |
| } |
| } |
| |
| MultiThreadedTest::MultiThreadedTest (EglTestContext& eglTestCtx, const char* name, const char* description, int threadCount, deUint64 timeoutUs) |
| : TestCase (eglTestCtx, name, description) |
| , m_threadCount (threadCount) |
| , m_initialized (false) |
| , m_startTimeUs (0) |
| , m_timeoutUs (timeoutUs) |
| , m_ok (true) |
| , m_supported (true) |
| , m_barrierWaiters (0) |
| , m_barrierSemaphore1 (0, 0) |
| , m_barrierSemaphore2 (1, 0) |
| |
| , m_display (EGL_NO_DISPLAY) |
| { |
| } |
| |
| MultiThreadedTest::~MultiThreadedTest (void) |
| { |
| for (int threadNdx = 0; threadNdx < (int)m_threads.size(); threadNdx++) |
| delete m_threads[threadNdx]; |
| m_threads.clear(); |
| } |
| |
| void MultiThreadedTest::init (void) |
| { |
| m_display = eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay()); |
| } |
| |
| void MultiThreadedTest::deinit (void) |
| { |
| if (m_display != EGL_NO_DISPLAY) |
| { |
| m_eglTestCtx.getLibrary().terminate(m_display); |
| m_display = EGL_NO_DISPLAY; |
| } |
| } |
| |
| void MultiThreadedTest::barrier (void) |
| { |
| { |
| const deInt32 waiters = deAtomicIncrement32(&m_barrierWaiters); |
| |
| if (waiters == m_threadCount) |
| { |
| m_barrierSemaphore2.decrement(); |
| m_barrierSemaphore1.increment(); |
| } |
| else |
| { |
| m_barrierSemaphore1.decrement(); |
| m_barrierSemaphore1.increment(); |
| } |
| } |
| |
| { |
| const deInt32 waiters = deAtomicDecrement32(&m_barrierWaiters); |
| |
| if (waiters == 0) |
| { |
| m_barrierSemaphore1.decrement(); |
| m_barrierSemaphore2.increment(); |
| } |
| else |
| { |
| m_barrierSemaphore2.decrement(); |
| m_barrierSemaphore2.increment(); |
| } |
| } |
| |
| // Barrier was released due an error in other thread |
| if (!m_ok || !m_supported) |
| throw TestThread::TestStop(); |
| } |
| |
| TestCase::IterateResult MultiThreadedTest::iterate (void) |
| { |
| if (!m_initialized) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Thread timeout limit: " << m_timeoutUs << "us" << tcu::TestLog::EndMessage; |
| |
| m_ok = true; |
| m_supported = true; |
| |
| // Create threads |
| m_threads.reserve(m_threadCount); |
| |
| for (int threadNdx = 0; threadNdx < m_threadCount; threadNdx++) |
| m_threads.push_back(new TestThread(*this, threadNdx)); |
| |
| m_startTimeUs = deGetMicroseconds(); |
| |
| // Run threads |
| for (int threadNdx = 0; threadNdx < (int)m_threads.size(); threadNdx++) |
| m_threads[threadNdx]->start(); |
| |
| m_initialized = true; |
| } |
| |
| int readyCount = 0; |
| for (int threadNdx = 0; threadNdx < (int)m_threads.size(); threadNdx++) |
| { |
| if (m_threads[threadNdx]->getStatus() != TestThread::THREADSTATUS_RUNNING) |
| readyCount++; |
| } |
| |
| if (readyCount == m_threadCount) |
| { |
| // Join threads |
| for (int threadNdx = 0; threadNdx < (int)m_threads.size(); threadNdx++) |
| m_threads[threadNdx]->join(); |
| |
| // Get logs |
| { |
| vector<int> messageNdx; |
| |
| messageNdx.resize(m_threads.size(), 0); |
| |
| while (true) |
| { |
| int nextThreadNdx = -1; |
| deUint64 nextThreadTimeUs = 0; |
| |
| for (int threadNdx = 0; threadNdx < (int)m_threads.size(); threadNdx++) |
| { |
| if (messageNdx[threadNdx] >= (int)m_threads[threadNdx]->getLog().getMessages().size()) |
| continue; |
| |
| if (nextThreadNdx == -1 || nextThreadTimeUs > m_threads[threadNdx]->getLog().getMessages()[messageNdx[threadNdx]].timeUs) |
| { |
| nextThreadNdx = threadNdx; |
| nextThreadTimeUs = m_threads[threadNdx]->getLog().getMessages()[messageNdx[threadNdx]].timeUs; |
| } |
| } |
| |
| if (nextThreadNdx == -1) |
| break; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "[" << (nextThreadTimeUs - m_startTimeUs) << "] (" << nextThreadNdx << ") " << m_threads[nextThreadNdx]->getLog().getMessages()[messageNdx[nextThreadNdx]].msg << tcu::TestLog::EndMessage; |
| |
| messageNdx[nextThreadNdx]++; |
| } |
| } |
| |
| // Destroy threads |
| for (int threadNdx = 0; threadNdx < (int)m_threads.size(); threadNdx++) |
| delete m_threads[threadNdx]; |
| |
| m_threads.clear(); |
| |
| // Set result |
| if (m_ok) |
| { |
| if (!m_supported) |
| m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); |
| else |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| } |
| else |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); |
| |
| return STOP; |
| } |
| else |
| { |
| // Check for timeout |
| const deUint64 currentTimeUs = deGetMicroseconds(); |
| |
| if (currentTimeUs - m_startTimeUs > m_timeoutUs) |
| { |
| // Get logs |
| { |
| vector<int> messageNdx; |
| |
| messageNdx.resize(m_threads.size(), 0); |
| |
| while (true) |
| { |
| int nextThreadNdx = -1; |
| deUint64 nextThreadTimeUs = 0; |
| |
| for (int threadNdx = 0; threadNdx < (int)m_threads.size(); threadNdx++) |
| { |
| if (messageNdx[threadNdx] >= (int)m_threads[threadNdx]->getLog().getMessages().size()) |
| continue; |
| |
| if (nextThreadNdx == -1 || nextThreadTimeUs > m_threads[threadNdx]->getLog().getMessages()[messageNdx[threadNdx]].timeUs) |
| { |
| nextThreadNdx = threadNdx; |
| nextThreadTimeUs = m_threads[threadNdx]->getLog().getMessages()[messageNdx[threadNdx]].timeUs; |
| } |
| } |
| |
| if (nextThreadNdx == -1) |
| break; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "[" << (nextThreadTimeUs - m_startTimeUs) << "] (" << nextThreadNdx << ") " << m_threads[nextThreadNdx]->getLog().getMessages()[messageNdx[nextThreadNdx]].msg << tcu::TestLog::EndMessage; |
| |
| messageNdx[nextThreadNdx]++; |
| } |
| } |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "[" << (currentTimeUs - m_startTimeUs) << "] (-) Timeout, Limit: " << m_timeoutUs << "us" << tcu::TestLog::EndMessage; |
| m_testCtx.getLog() << tcu::TestLog::Message << "[" << (currentTimeUs - m_startTimeUs) << "] (-) Trying to perform resource cleanup..." << tcu::TestLog::EndMessage; |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); |
| return STOP; |
| } |
| |
| // Sleep |
| deSleep(10); |
| } |
| |
| return CONTINUE; |
| } |
| |
| namespace |
| { |
| |
| const char* configAttributeToString (EGLint e) |
| { |
| switch (e) |
| { |
| case EGL_BUFFER_SIZE: return "EGL_BUFFER_SIZE"; |
| case EGL_RED_SIZE: return "EGL_RED_SIZE"; |
| case EGL_GREEN_SIZE: return "EGL_GREEN_SIZE"; |
| case EGL_BLUE_SIZE: return "EGL_BLUE_SIZE"; |
| case EGL_LUMINANCE_SIZE: return "EGL_LUMINANCE_SIZE"; |
| case EGL_ALPHA_SIZE: return "EGL_ALPHA_SIZE"; |
| case EGL_ALPHA_MASK_SIZE: return "EGL_ALPHA_MASK_SIZE"; |
| case EGL_BIND_TO_TEXTURE_RGB: return "EGL_BIND_TO_TEXTURE_RGB"; |
| case EGL_BIND_TO_TEXTURE_RGBA: return "EGL_BIND_TO_TEXTURE_RGBA"; |
| case EGL_COLOR_BUFFER_TYPE: return "EGL_COLOR_BUFFER_TYPE"; |
| case EGL_CONFIG_CAVEAT: return "EGL_CONFIG_CAVEAT"; |
| case EGL_CONFIG_ID: return "EGL_CONFIG_ID"; |
| case EGL_CONFORMANT: return "EGL_CONFORMANT"; |
| case EGL_DEPTH_SIZE: return "EGL_DEPTH_SIZE"; |
| case EGL_LEVEL: return "EGL_LEVEL"; |
| case EGL_MAX_PBUFFER_WIDTH: return "EGL_MAX_PBUFFER_WIDTH"; |
| case EGL_MAX_PBUFFER_HEIGHT: return "EGL_MAX_PBUFFER_HEIGHT"; |
| case EGL_MAX_PBUFFER_PIXELS: return "EGL_MAX_PBUFFER_PIXELS"; |
| case EGL_MAX_SWAP_INTERVAL: return "EGL_MAX_SWAP_INTERVAL"; |
| case EGL_MIN_SWAP_INTERVAL: return "EGL_MIN_SWAP_INTERVAL"; |
| case EGL_NATIVE_RENDERABLE: return "EGL_NATIVE_RENDERABLE"; |
| case EGL_NATIVE_VISUAL_ID: return "EGL_NATIVE_VISUAL_ID"; |
| case EGL_NATIVE_VISUAL_TYPE: return "EGL_NATIVE_VISUAL_TYPE"; |
| case EGL_RENDERABLE_TYPE: return "EGL_RENDERABLE_TYPE"; |
| case EGL_SAMPLE_BUFFERS: return "EGL_SAMPLE_BUFFERS"; |
| case EGL_SAMPLES: return "EGL_SAMPLES"; |
| case EGL_STENCIL_SIZE: return "EGL_STENCIL_SIZE"; |
| case EGL_SURFACE_TYPE: return "EGL_SURFACE_TYPE"; |
| case EGL_TRANSPARENT_TYPE: return "EGL_TRANSPARENT_TYPE"; |
| case EGL_TRANSPARENT_RED_VALUE: return "EGL_TRANSPARENT_RED_VALUE"; |
| case EGL_TRANSPARENT_GREEN_VALUE: return "EGL_TRANSPARENT_GREEN_VALUE"; |
| case EGL_TRANSPARENT_BLUE_VALUE: return "EGL_TRANSPARENT_BLUE_VALUE"; |
| default: return "<Unknown>"; |
| } |
| } |
| |
| } // anonymous |
| |
| class MultiThreadedConfigTest : public MultiThreadedTest |
| { |
| public: |
| MultiThreadedConfigTest (EglTestContext& context, const char* name, const char* description, int getConfigs, int chooseConfigs, int query); |
| bool runThread (TestThread& thread); |
| |
| private: |
| const int m_getConfigs; |
| const int m_chooseConfigs; |
| const int m_query; |
| }; |
| |
| MultiThreadedConfigTest::MultiThreadedConfigTest (EglTestContext& context, const char* name, const char* description, int getConfigs, int chooseConfigs, int query) |
| : MultiThreadedTest (context, name, description, 2, 20000000/*us = 20s*/) // \todo [mika] Set timeout to something relevant to frameworks timeout? |
| , m_getConfigs (getConfigs) |
| , m_chooseConfigs (chooseConfigs) |
| , m_query (query) |
| { |
| } |
| |
| bool MultiThreadedConfigTest::runThread (TestThread& thread) |
| { |
| const Library& egl = getLibrary(); |
| de::Random rnd (deInt32Hash(thread.getId() + 10435)); |
| vector<EGLConfig> configs; |
| |
| barrier(); |
| |
| for (int getConfigsNdx = 0; getConfigsNdx < m_getConfigs; getConfigsNdx++) |
| { |
| EGLint configCount; |
| |
| // Get number of configs |
| { |
| EGLBoolean result; |
| |
| result = egl.getConfigs(m_display, NULL, 0, &configCount); |
| thread.getLog() << ThreadLog::BeginMessage << result << " = eglGetConfigs(" << m_display << ", NULL, 0, " << configCount << ")" << ThreadLog::EndMessage; |
| EGLU_CHECK_MSG(egl, "eglGetConfigs()"); |
| |
| if (!result) |
| return false; |
| } |
| |
| configs.resize(configs.size() + configCount); |
| |
| // Get configs |
| if (configCount != 0) |
| { |
| EGLBoolean result; |
| |
| result = egl.getConfigs(m_display, &(configs[configs.size() - configCount]), configCount, &configCount); |
| thread.getLog() << ThreadLog::BeginMessage << result << " = eglGetConfigs(" << m_display << ", &configs' " << configCount << ", " << configCount << ")" << ThreadLog::EndMessage; |
| EGLU_CHECK_MSG(egl, "eglGetConfigs()"); |
| |
| if (!result) |
| return false; |
| } |
| |
| // Pop configs to stop config list growing |
| if (configs.size() > 40) |
| { |
| configs.erase(configs.begin() + 40, configs.end()); |
| } |
| else |
| { |
| const int popCount = rnd.getInt(0, (int)(configs.size()-2)); |
| |
| configs.erase(configs.begin() + (configs.size() - popCount), configs.end()); |
| } |
| } |
| |
| for (int chooseConfigsNdx = 0; chooseConfigsNdx < m_chooseConfigs; chooseConfigsNdx++) |
| { |
| EGLint configCount; |
| |
| static const EGLint attribList[] = { |
| EGL_NONE |
| }; |
| |
| // Get number of configs |
| { |
| EGLBoolean result; |
| |
| result = egl.chooseConfig(m_display, attribList, NULL, 0, &configCount); |
| thread.getLog() << ThreadLog::BeginMessage << result << " = eglChooseConfig(" << m_display << ", { EGL_NONE }, NULL, 0, " << configCount << ")" << ThreadLog::EndMessage; |
| EGLU_CHECK_MSG(egl, "eglChooseConfig()"); |
| |
| if (!result) |
| return false; |
| } |
| |
| configs.resize(configs.size() + configCount); |
| |
| // Get configs |
| if (configCount != 0) |
| { |
| EGLBoolean result; |
| |
| result = egl.chooseConfig(m_display, attribList, &(configs[configs.size() - configCount]), configCount, &configCount); |
| thread.getLog() << ThreadLog::BeginMessage << result << " = eglChooseConfig(" << m_display << ", { EGL_NONE }, &configs, " << configCount << ", " << configCount << ")" << ThreadLog::EndMessage; |
| EGLU_CHECK_MSG(egl, "eglChooseConfig()"); |
| |
| if (!result) |
| return false; |
| } |
| |
| // Pop configs to stop config list growing |
| if (configs.size() > 40) |
| { |
| configs.erase(configs.begin() + 40, configs.end()); |
| } |
| else |
| { |
| const int popCount = rnd.getInt(0, (int)(configs.size()-2)); |
| |
| configs.erase(configs.begin() + (configs.size() - popCount), configs.end()); |
| } |
| } |
| |
| { |
| // Perform queries on configs |
| static const EGLint attributes[] = |
| { |
| EGL_BUFFER_SIZE, |
| EGL_RED_SIZE, |
| EGL_GREEN_SIZE, |
| EGL_BLUE_SIZE, |
| EGL_LUMINANCE_SIZE, |
| EGL_ALPHA_SIZE, |
| EGL_ALPHA_MASK_SIZE, |
| EGL_BIND_TO_TEXTURE_RGB, |
| EGL_BIND_TO_TEXTURE_RGBA, |
| EGL_COLOR_BUFFER_TYPE, |
| EGL_CONFIG_CAVEAT, |
| EGL_CONFIG_ID, |
| EGL_CONFORMANT, |
| EGL_DEPTH_SIZE, |
| EGL_LEVEL, |
| EGL_MAX_PBUFFER_WIDTH, |
| EGL_MAX_PBUFFER_HEIGHT, |
| EGL_MAX_PBUFFER_PIXELS, |
| EGL_MAX_SWAP_INTERVAL, |
| EGL_MIN_SWAP_INTERVAL, |
| EGL_NATIVE_RENDERABLE, |
| EGL_NATIVE_VISUAL_ID, |
| EGL_NATIVE_VISUAL_TYPE, |
| EGL_RENDERABLE_TYPE, |
| EGL_SAMPLE_BUFFERS, |
| EGL_SAMPLES, |
| EGL_STENCIL_SIZE, |
| EGL_SURFACE_TYPE, |
| EGL_TRANSPARENT_TYPE, |
| EGL_TRANSPARENT_RED_VALUE, |
| EGL_TRANSPARENT_GREEN_VALUE, |
| EGL_TRANSPARENT_BLUE_VALUE |
| }; |
| |
| for (int queryNdx = 0; queryNdx < m_query; queryNdx++) |
| { |
| const EGLint attribute = attributes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(attributes)-1)]; |
| EGLConfig config = configs[rnd.getInt(0, (int)(configs.size()-1))]; |
| EGLint value; |
| EGLBoolean result; |
| |
| result = egl.getConfigAttrib(m_display, config, attribute, &value); |
| thread.getLog() << ThreadLog::BeginMessage << result << " = eglGetConfigAttrib(" << m_display << ", " << config << ", " << configAttributeToString(attribute) << ", " << value << ")" << ThreadLog::EndMessage; |
| EGLU_CHECK_MSG(egl, "eglGetConfigAttrib()"); |
| |
| if (!result) |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| class MultiThreadedObjectTest : public MultiThreadedTest |
| { |
| public: |
| enum Type |
| { |
| TYPE_PBUFFER = (1<<0), |
| TYPE_PIXMAP = (1<<1), |
| TYPE_WINDOW = (1<<2), |
| TYPE_SINGLE_WINDOW = (1<<3), |
| TYPE_CONTEXT = (1<<4) |
| }; |
| |
| MultiThreadedObjectTest (EglTestContext& context, const char* name, const char* description, deUint32 types); |
| ~MultiThreadedObjectTest (void); |
| |
| virtual void deinit (void); |
| |
| bool runThread (TestThread& thread); |
| |
| void createDestroyObjects (TestThread& thread, int count); |
| void pushObjectsToShared (TestThread& thread); |
| void pullObjectsFromShared (TestThread& thread, int pbufferCount, int pixmapCount, int windowCount, int contextCount); |
| void querySetSharedObjects (TestThread& thread, int count); |
| void destroyObjects (TestThread& thread); |
| |
| private: |
| EGLConfig m_config; |
| de::Random m_rnd0; |
| de::Random m_rnd1; |
| Type m_types; |
| |
| volatile deUint32 m_hasWindow; |
| |
| vector<pair<eglu::NativePixmap*, EGLSurface> > m_sharedNativePixmaps; |
| vector<pair<eglu::NativePixmap*, EGLSurface> > m_nativePixmaps0; |
| vector<pair<eglu::NativePixmap*, EGLSurface> > m_nativePixmaps1; |
| |
| vector<pair<eglu::NativeWindow*, EGLSurface> > m_sharedNativeWindows; |
| vector<pair<eglu::NativeWindow*, EGLSurface> > m_nativeWindows0; |
| vector<pair<eglu::NativeWindow*, EGLSurface> > m_nativeWindows1; |
| |
| vector<EGLSurface> m_sharedPbuffers; |
| vector<EGLSurface> m_pbuffers0; |
| vector<EGLSurface> m_pbuffers1; |
| |
| vector<EGLContext> m_sharedContexts; |
| vector<EGLContext> m_contexts0; |
| vector<EGLContext> m_contexts1; |
| }; |
| |
| MultiThreadedObjectTest::MultiThreadedObjectTest (EglTestContext& context, const char* name, const char* description, deUint32 type) |
| : MultiThreadedTest (context, name, description, 2, 20000000/*us = 20s*/) // \todo [mika] Set timeout to something relevant to frameworks timeout? |
| , m_config (DE_NULL) |
| , m_rnd0 (58204327) |
| , m_rnd1 (230983) |
| , m_types ((Type)type) |
| , m_hasWindow (0) |
| { |
| } |
| |
| MultiThreadedObjectTest::~MultiThreadedObjectTest (void) |
| { |
| deinit(); |
| } |
| |
| void MultiThreadedObjectTest::deinit (void) |
| { |
| const Library& egl = getLibrary(); |
| |
| // Clear pbuffers |
| for (int pbufferNdx = 0; pbufferNdx < (int)m_pbuffers0.size(); pbufferNdx++) |
| { |
| if (m_pbuffers0[pbufferNdx] != EGL_NO_SURFACE) |
| { |
| egl.destroySurface(m_display, m_pbuffers0[pbufferNdx]); |
| EGLU_CHECK_MSG(egl, "eglDestroySurface()"); |
| m_pbuffers0[pbufferNdx] = EGL_NO_SURFACE; |
| } |
| } |
| m_pbuffers0.clear(); |
| |
| for (int pbufferNdx = 0; pbufferNdx < (int)m_pbuffers1.size(); pbufferNdx++) |
| { |
| if (m_pbuffers1[pbufferNdx] != EGL_NO_SURFACE) |
| { |
| egl.destroySurface(m_display, m_pbuffers1[pbufferNdx]); |
| EGLU_CHECK_MSG(egl, "eglDestroySurface()"); |
| m_pbuffers1[pbufferNdx] = EGL_NO_SURFACE; |
| } |
| } |
| m_pbuffers1.clear(); |
| |
| for (int pbufferNdx = 0; pbufferNdx < (int)m_sharedPbuffers.size(); pbufferNdx++) |
| { |
| if (m_sharedPbuffers[pbufferNdx] != EGL_NO_SURFACE) |
| { |
| egl.destroySurface(m_display, m_sharedPbuffers[pbufferNdx]); |
| EGLU_CHECK_MSG(egl, "eglDestroySurface()"); |
| m_sharedPbuffers[pbufferNdx] = EGL_NO_SURFACE; |
| } |
| } |
| m_sharedPbuffers.clear(); |
| |
| for (int contextNdx = 0; contextNdx < (int)m_sharedContexts.size(); contextNdx++) |
| { |
| if (m_sharedContexts[contextNdx] != EGL_NO_CONTEXT) |
| { |
| egl.destroyContext(m_display, m_sharedContexts[contextNdx]); |
| EGLU_CHECK_MSG(egl, "eglDestroyContext()"); |
| m_sharedContexts[contextNdx] = EGL_NO_CONTEXT; |
| } |
| } |
| m_sharedContexts.clear(); |
| |
| for (int contextNdx = 0; contextNdx < (int)m_contexts0.size(); contextNdx++) |
| { |
| if (m_contexts0[contextNdx] != EGL_NO_CONTEXT) |
| { |
| egl.destroyContext(m_display, m_contexts0[contextNdx]); |
| EGLU_CHECK_MSG(egl, "eglDestroyContext()"); |
| m_contexts0[contextNdx] = EGL_NO_CONTEXT; |
| } |
| } |
| m_contexts0.clear(); |
| |
| for (int contextNdx = 0; contextNdx < (int)m_contexts1.size(); contextNdx++) |
| { |
| if (m_contexts1[contextNdx] != EGL_NO_CONTEXT) |
| { |
| egl.destroyContext(m_display, m_contexts1[contextNdx]); |
| EGLU_CHECK_MSG(egl, "eglDestroyContext()"); |
| m_contexts1[contextNdx] = EGL_NO_CONTEXT; |
| } |
| } |
| m_contexts1.clear(); |
| |
| // Clear pixmaps |
| for (int pixmapNdx = 0; pixmapNdx < (int)m_nativePixmaps0.size(); pixmapNdx++) |
| { |
| if (m_nativePixmaps0[pixmapNdx].second != EGL_NO_SURFACE) |
| EGLU_CHECK_CALL(egl, destroySurface(m_display, m_nativePixmaps0[pixmapNdx].second)); |
| |
| m_nativePixmaps0[pixmapNdx].second = EGL_NO_SURFACE; |
| delete m_nativePixmaps0[pixmapNdx].first; |
| m_nativePixmaps0[pixmapNdx].first = NULL; |
| } |
| m_nativePixmaps0.clear(); |
| |
| for (int pixmapNdx = 0; pixmapNdx < (int)m_nativePixmaps1.size(); pixmapNdx++) |
| { |
| if (m_nativePixmaps1[pixmapNdx].second != EGL_NO_SURFACE) |
| EGLU_CHECK_CALL(egl, destroySurface(m_display, m_nativePixmaps1[pixmapNdx].second)); |
| |
| m_nativePixmaps1[pixmapNdx].second = EGL_NO_SURFACE; |
| delete m_nativePixmaps1[pixmapNdx].first; |
| m_nativePixmaps1[pixmapNdx].first = NULL; |
| } |
| m_nativePixmaps1.clear(); |
| |
| for (int pixmapNdx = 0; pixmapNdx < (int)m_sharedNativePixmaps.size(); pixmapNdx++) |
| { |
| if (m_sharedNativePixmaps[pixmapNdx].second != EGL_NO_SURFACE) |
| EGLU_CHECK_CALL(egl, destroySurface(m_display, m_sharedNativePixmaps[pixmapNdx].second)); |
| |
| m_sharedNativePixmaps[pixmapNdx].second = EGL_NO_SURFACE; |
| delete m_sharedNativePixmaps[pixmapNdx].first; |
| m_sharedNativePixmaps[pixmapNdx].first = NULL; |
| } |
| m_sharedNativePixmaps.clear(); |
| |
| // Clear windows |
| for (int windowNdx = 0; windowNdx < (int)m_nativeWindows1.size(); windowNdx++) |
| { |
| if (m_nativeWindows1[windowNdx].second != EGL_NO_SURFACE) |
| EGLU_CHECK_CALL(egl, destroySurface(m_display, m_nativeWindows1[windowNdx].second)); |
| |
| m_nativeWindows1[windowNdx].second = EGL_NO_SURFACE; |
| delete m_nativeWindows1[windowNdx].first; |
| m_nativeWindows1[windowNdx].first = NULL; |
| } |
| m_nativeWindows1.clear(); |
| |
| for (int windowNdx = 0; windowNdx < (int)m_nativeWindows0.size(); windowNdx++) |
| { |
| if (m_nativeWindows0[windowNdx].second != EGL_NO_SURFACE) |
| EGLU_CHECK_CALL(egl, destroySurface(m_display, m_nativeWindows0[windowNdx].second)); |
| |
| m_nativeWindows0[windowNdx].second = EGL_NO_SURFACE; |
| delete m_nativeWindows0[windowNdx].first; |
| m_nativeWindows0[windowNdx].first = NULL; |
| } |
| m_nativeWindows0.clear(); |
| |
| for (int windowNdx = 0; windowNdx < (int)m_sharedNativeWindows.size(); windowNdx++) |
| { |
| if (m_sharedNativeWindows[windowNdx].second != EGL_NO_SURFACE) |
| EGLU_CHECK_CALL(egl, destroySurface(m_display, m_sharedNativeWindows[windowNdx].second)); |
| |
| m_sharedNativeWindows[windowNdx].second = EGL_NO_SURFACE; |
| delete m_sharedNativeWindows[windowNdx].first; |
| m_sharedNativeWindows[windowNdx].first = NULL; |
| } |
| m_sharedNativeWindows.clear(); |
| |
| MultiThreadedTest::deinit(); |
| } |
| |
| bool MultiThreadedObjectTest::runThread (TestThread& thread) |
| { |
| const Library& egl = getLibrary(); |
| |
| if (thread.getId() == 0) |
| { |
| EGLint surfaceTypes = 0; |
| |
| if ((m_types & TYPE_WINDOW) != 0) |
| surfaceTypes |= EGL_WINDOW_BIT; |
| |
| if ((m_types & TYPE_PBUFFER) != 0) |
| surfaceTypes |= EGL_PBUFFER_BIT; |
| |
| if ((m_types & TYPE_PIXMAP) != 0) |
| surfaceTypes |= EGL_PIXMAP_BIT; |
| |
| EGLint configCount; |
| EGLint attribList[] = |
| { |
| EGL_SURFACE_TYPE, surfaceTypes, |
| EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, |
| EGL_NONE |
| }; |
| |
| EGLU_CHECK_CALL(egl, chooseConfig(m_display, attribList, &m_config, 1, &configCount)); |
| |
| if (configCount == 0) |
| TCU_THROW(NotSupportedError, "No usable config found"); |
| } |
| |
| barrier(); |
| |
| // Create / Destroy Objects |
| if ((m_types & TYPE_SINGLE_WINDOW) != 0 && (m_types & TYPE_PBUFFER) == 0 && (m_types & TYPE_PIXMAP) == 0 && (m_types & TYPE_CONTEXT) == 0) |
| { |
| if (thread.getId() == 0) |
| createDestroyObjects(thread, 1); |
| } |
| else |
| createDestroyObjects(thread, 100); |
| |
| // Push first threads objects to shared |
| if (thread.getId() == 0) |
| pushObjectsToShared(thread); |
| |
| barrier(); |
| |
| // Push second threads objects to shared |
| if (thread.getId() == 1) |
| pushObjectsToShared(thread); |
| |
| barrier(); |
| |
| // Make queries from shared surfaces |
| querySetSharedObjects(thread, 100); |
| |
| barrier(); |
| |
| // Pull surfaces for first thread from shared surfaces |
| if (thread.getId() == 0) |
| pullObjectsFromShared(thread, (int)(m_sharedPbuffers.size()/2), (int)(m_sharedNativePixmaps.size()/2), (int)(m_sharedNativeWindows.size()/2), (int)(m_sharedContexts.size()/2)); |
| |
| barrier(); |
| |
| // Pull surfaces for second thread from shared surfaces |
| if (thread.getId() == 1) |
| pullObjectsFromShared(thread, (int)m_sharedPbuffers.size(), (int)m_sharedNativePixmaps.size(), (int)m_sharedNativeWindows.size(), (int)m_sharedContexts.size()); |
| |
| barrier(); |
| |
| // Create / Destroy Objects |
| if ((m_types & TYPE_SINGLE_WINDOW) == 0) |
| createDestroyObjects(thread, 100); |
| |
| // Destroy surfaces |
| destroyObjects(thread); |
| |
| return true; |
| } |
| |
| void MultiThreadedObjectTest::createDestroyObjects (TestThread& thread, int count) |
| { |
| const Library& egl = getLibrary(); |
| de::Random& rnd = (thread.getId() == 0 ? m_rnd0 : m_rnd1); |
| vector<EGLSurface>& pbuffers = (thread.getId() == 0 ? m_pbuffers0 : m_pbuffers1); |
| vector<pair<eglu::NativeWindow*, EGLSurface> >& windows = (thread.getId() == 0 ? m_nativeWindows0 : m_nativeWindows1); |
| vector<pair<eglu::NativePixmap*, EGLSurface> >& pixmaps = (thread.getId() == 0 ? m_nativePixmaps0 : m_nativePixmaps1); |
| vector<EGLContext>& contexts = (thread.getId() == 0 ? m_contexts0 : m_contexts1); |
| set<Type> objectTypes; |
| |
| if ((m_types & TYPE_PBUFFER) != 0) |
| objectTypes.insert(TYPE_PBUFFER); |
| |
| if ((m_types & TYPE_PIXMAP) != 0) |
| objectTypes.insert(TYPE_PIXMAP); |
| |
| if ((m_types & TYPE_WINDOW) != 0) |
| objectTypes.insert(TYPE_WINDOW); |
| |
| if ((m_types & TYPE_CONTEXT) != 0) |
| objectTypes.insert(TYPE_CONTEXT); |
| |
| for (int createDestroyNdx = 0; createDestroyNdx < count; createDestroyNdx++) |
| { |
| bool create; |
| Type type; |
| |
| if (pbuffers.size() > 5 && ((m_types & TYPE_PBUFFER) != 0)) |
| { |
| create = false; |
| type = TYPE_PBUFFER; |
| } |
| else if (windows.size() > 5 && ((m_types & TYPE_WINDOW) != 0)) |
| { |
| create = false; |
| type = TYPE_WINDOW; |
| } |
| else if (pixmaps.size() > 5 && ((m_types & TYPE_PIXMAP) != 0)) |
| { |
| create = false; |
| type = TYPE_PIXMAP; |
| } |
| else if (contexts.size() > 5 && ((m_types & TYPE_CONTEXT) != 0)) |
| { |
| create = false; |
| type = TYPE_CONTEXT; |
| } |
| else if (pbuffers.size() < 3 && ((m_types & TYPE_PBUFFER) != 0)) |
| { |
| create = true; |
| type = TYPE_PBUFFER; |
| } |
| else if (pixmaps.size() < 3 && ((m_types & TYPE_PIXMAP) != 0)) |
| { |
| create = true; |
| type = TYPE_PIXMAP; |
| } |
| else if (contexts.size() < 3 && ((m_types & TYPE_CONTEXT) != 0)) |
| { |
| create = true; |
| type = TYPE_CONTEXT; |
| } |
| else if (windows.size() < 3 && ((m_types & TYPE_WINDOW) != 0) && ((m_types & TYPE_SINGLE_WINDOW) == 0)) |
| { |
| create = true; |
| type = TYPE_WINDOW; |
| } |
| else if (windows.empty() && (m_hasWindow == 0) && ((m_types & TYPE_WINDOW) != 0) && ((m_types & TYPE_SINGLE_WINDOW) != 0)) |
| { |
| create = true; |
| type = TYPE_WINDOW; |
| } |
| else |
| { |
| create = rnd.getBool(); |
| |
| if (!create && windows.empty()) |
| objectTypes.erase(TYPE_WINDOW); |
| |
| type = rnd.choose<Type>(objectTypes.begin(), objectTypes.end()); |
| } |
| |
| if (create) |
| { |
| switch (type) |
| { |
| case TYPE_PBUFFER: |
| { |
| EGLSurface surface; |
| |
| const EGLint attributes[] = |
| { |
| EGL_WIDTH, 64, |
| EGL_HEIGHT, 64, |
| |
| EGL_NONE |
| }; |
| |
| surface = egl.createPbufferSurface(m_display, m_config, attributes); |
| thread.getLog() << ThreadLog::BeginMessage << surface << " = eglCreatePbufferSurface(" << m_display << ", " << m_config << ", { EGL_WIDTH, 64, EGL_HEIGHT, 64, EGL_NONE })" << ThreadLog::EndMessage; |
| EGLU_CHECK_MSG(egl, "eglCreatePbufferSurface()"); |
| |
| pbuffers.push_back(surface); |
| |
| break; |
| } |
| |
| case TYPE_WINDOW: |
| { |
| const eglu::NativeWindowFactory& windowFactory = eglu::selectNativeWindowFactory(m_eglTestCtx.getNativeDisplayFactory(), m_testCtx.getCommandLine()); |
| |
| if ((m_types & TYPE_SINGLE_WINDOW) != 0) |
| { |
| if (deAtomicCompareExchange32(&m_hasWindow, 0, 1) == 0) |
| { |
| eglu::NativeWindow* window = DE_NULL; |
| EGLSurface surface = EGL_NO_SURFACE; |
| |
| try |
| { |
| window = windowFactory.createWindow(&m_eglTestCtx.getNativeDisplay(), m_display, m_config, DE_NULL, eglu::WindowParams(64, 64, eglu::parseWindowVisibility(m_testCtx.getCommandLine()))); |
| surface = eglu::createWindowSurface(m_eglTestCtx.getNativeDisplay(), *window, m_display, m_config, DE_NULL); |
| |
| thread.getLog() << ThreadLog::BeginMessage << surface << " = eglCreateWindowSurface()" << ThreadLog::EndMessage; |
| windows.push_back(std::make_pair(window, surface)); |
| } |
| catch (const std::exception&) |
| { |
| if (surface != EGL_NO_SURFACE) |
| EGLU_CHECK_CALL(egl, destroySurface(m_display, surface)); |
| delete window; |
| m_hasWindow = 0; |
| throw; |
| } |
| } |
| else |
| { |
| createDestroyNdx--; |
| } |
| } |
| else |
| { |
| eglu::NativeWindow* window = DE_NULL; |
| EGLSurface surface = EGL_NO_SURFACE; |
| |
| try |
| { |
| window = windowFactory.createWindow(&m_eglTestCtx.getNativeDisplay(), m_display, m_config, DE_NULL, eglu::WindowParams(64, 64, eglu::parseWindowVisibility(m_testCtx.getCommandLine()))); |
| surface = eglu::createWindowSurface(m_eglTestCtx.getNativeDisplay(), *window, m_display, m_config, DE_NULL); |
| |
| thread.getLog() << ThreadLog::BeginMessage << surface << " = eglCreateWindowSurface()" << ThreadLog::EndMessage; |
| windows.push_back(std::make_pair(window, surface)); |
| } |
| catch (const std::exception&) |
| { |
| if (surface != EGL_NO_SURFACE) |
| EGLU_CHECK_CALL(egl, destroySurface(m_display, surface)); |
| delete window; |
| throw; |
| } |
| } |
| break; |
| } |
| |
| case TYPE_PIXMAP: |
| { |
| const eglu::NativePixmapFactory& pixmapFactory = eglu::selectNativePixmapFactory(m_eglTestCtx.getNativeDisplayFactory(), m_testCtx.getCommandLine()); |
| eglu::NativePixmap* pixmap = DE_NULL; |
| EGLSurface surface = EGL_NO_SURFACE; |
| |
| try |
| { |
| pixmap = pixmapFactory.createPixmap(&m_eglTestCtx.getNativeDisplay(), m_display, m_config, DE_NULL, 64, 64); |
| surface = eglu::createPixmapSurface(m_eglTestCtx.getNativeDisplay(), *pixmap, m_display, m_config, DE_NULL); |
| |
| thread.getLog() << ThreadLog::BeginMessage << surface << " = eglCreatePixmapSurface()" << ThreadLog::EndMessage; |
| pixmaps.push_back(std::make_pair(pixmap, surface)); |
| } |
| catch (const std::exception&) |
| { |
| if (surface != EGL_NO_SURFACE) |
| EGLU_CHECK_CALL(egl, destroySurface(m_display, surface)); |
| delete pixmap; |
| throw; |
| } |
| break; |
| } |
| |
| case TYPE_CONTEXT: |
| { |
| EGLContext context; |
| |
| EGLU_CHECK_CALL(egl, bindAPI(EGL_OPENGL_ES_API)); |
| thread.getLog() << ThreadLog::BeginMessage << "eglBindAPI(EGL_OPENGL_ES_API)" << ThreadLog::EndMessage; |
| |
| const EGLint attributes[] = |
| { |
| EGL_CONTEXT_CLIENT_VERSION, 2, |
| EGL_NONE |
| }; |
| |
| context = egl.createContext(m_display, m_config, EGL_NO_CONTEXT, attributes); |
| thread.getLog() << ThreadLog::BeginMessage << context << " = eglCreateContext(" << m_display << ", " << m_config << ", EGL_NO_CONTEXT, { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE })" << ThreadLog::EndMessage; |
| EGLU_CHECK_MSG(egl, "eglCreateContext()"); |
| contexts.push_back(context); |
| break; |
| } |
| |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| else |
| { |
| switch (type) |
| { |
| case TYPE_PBUFFER: |
| { |
| const int pbufferNdx = rnd.getInt(0, (int)(pbuffers.size()-1)); |
| EGLBoolean result; |
| |
| result = egl.destroySurface(m_display, pbuffers[pbufferNdx]); |
| thread.getLog() << ThreadLog::BeginMessage << result << " = eglDestroySurface(" << m_display << ", " << pbuffers[pbufferNdx] << ")" << ThreadLog::EndMessage; |
| EGLU_CHECK_MSG(egl, "eglDestroySurface()"); |
| |
| pbuffers.erase(pbuffers.begin() + pbufferNdx); |
| |
| break; |
| } |
| |
| case TYPE_WINDOW: |
| { |
| const int windowNdx = rnd.getInt(0, (int)(windows.size()-1)); |
| |
| thread.getLog() << ThreadLog::BeginMessage << "eglDestroySurface(" << m_display << ", " << windows[windowNdx].second << ")" << ThreadLog::EndMessage; |
| |
| EGLU_CHECK_CALL(egl, destroySurface(m_display, windows[windowNdx].second)); |
| windows[windowNdx].second = EGL_NO_SURFACE; |
| delete windows[windowNdx].first; |
| windows[windowNdx].first = DE_NULL; |
| windows.erase(windows.begin() + windowNdx); |
| |
| if ((m_types & TYPE_SINGLE_WINDOW) != 0) |
| m_hasWindow = 0; |
| |
| break; |
| } |
| |
| case TYPE_PIXMAP: |
| { |
| const int pixmapNdx = rnd.getInt(0, (int)(pixmaps.size()-1)); |
| |
| thread.getLog() << ThreadLog::BeginMessage << "eglDestroySurface(" << m_display << ", " << pixmaps[pixmapNdx].second << ")" << ThreadLog::EndMessage; |
| EGLU_CHECK_CALL(egl, destroySurface(m_display, pixmaps[pixmapNdx].second)); |
| pixmaps[pixmapNdx].second = EGL_NO_SURFACE; |
| delete pixmaps[pixmapNdx].first; |
| pixmaps[pixmapNdx].first = DE_NULL; |
| pixmaps.erase(pixmaps.begin() + pixmapNdx); |
| |
| break; |
| } |
| |
| case TYPE_CONTEXT: |
| { |
| const int contextNdx = rnd.getInt(0, (int)(contexts.size()-1)); |
| |
| EGLU_CHECK_CALL(egl, destroyContext(m_display, contexts[contextNdx])); |
| thread.getLog() << ThreadLog::BeginMessage << "eglDestroyContext(" << m_display << ", " << contexts[contextNdx] << ")" << ThreadLog::EndMessage; |
| contexts.erase(contexts.begin() + contextNdx); |
| |
| break; |
| } |
| |
| default: |
| DE_ASSERT(false); |
| } |
| |
| } |
| } |
| } |
| |
| void MultiThreadedObjectTest::pushObjectsToShared (TestThread& thread) |
| { |
| vector<EGLSurface>& pbuffers = (thread.getId() == 0 ? m_pbuffers0 : m_pbuffers1); |
| vector<pair<eglu::NativeWindow*, EGLSurface> >& windows = (thread.getId() == 0 ? m_nativeWindows0 : m_nativeWindows1); |
| vector<pair<eglu::NativePixmap*, EGLSurface> >& pixmaps = (thread.getId() == 0 ? m_nativePixmaps0 : m_nativePixmaps1); |
| vector<EGLContext>& contexts = (thread.getId() == 0 ? m_contexts0 : m_contexts1); |
| |
| for (int pbufferNdx = 0; pbufferNdx < (int)pbuffers.size(); pbufferNdx++) |
| m_sharedPbuffers.push_back(pbuffers[pbufferNdx]); |
| |
| pbuffers.clear(); |
| |
| for (int windowNdx = 0; windowNdx < (int)windows.size(); windowNdx++) |
| m_sharedNativeWindows.push_back(windows[windowNdx]); |
| |
| windows.clear(); |
| |
| for (int pixmapNdx = 0; pixmapNdx < (int)pixmaps.size(); pixmapNdx++) |
| m_sharedNativePixmaps.push_back(pixmaps[pixmapNdx]); |
| |
| pixmaps.clear(); |
| |
| for (int contextNdx = 0; contextNdx < (int)contexts.size(); contextNdx++) |
| m_sharedContexts.push_back(contexts[contextNdx]); |
| |
| contexts.clear(); |
| } |
| |
| void MultiThreadedObjectTest::pullObjectsFromShared (TestThread& thread, int pbufferCount, int pixmapCount, int windowCount, int contextCount) |
| { |
| de::Random& rnd = (thread.getId() == 0 ? m_rnd0 : m_rnd1); |
| vector<EGLSurface>& pbuffers = (thread.getId() == 0 ? m_pbuffers0 : m_pbuffers1); |
| vector<pair<eglu::NativeWindow*, EGLSurface> >& windows = (thread.getId() == 0 ? m_nativeWindows0 : m_nativeWindows1); |
| vector<pair<eglu::NativePixmap*, EGLSurface> >& pixmaps = (thread.getId() == 0 ? m_nativePixmaps0 : m_nativePixmaps1); |
| vector<EGLContext>& contexts = (thread.getId() == 0 ? m_contexts0 : m_contexts1); |
| |
| for (int pbufferNdx = 0; pbufferNdx < pbufferCount; pbufferNdx++) |
| { |
| const int ndx = rnd.getInt(0, (int)(m_sharedPbuffers.size()-1)); |
| |
| pbuffers.push_back(m_sharedPbuffers[ndx]); |
| m_sharedPbuffers.erase(m_sharedPbuffers.begin() + ndx); |
| } |
| |
| for (int pixmapNdx = 0; pixmapNdx < pixmapCount; pixmapNdx++) |
| { |
| const int ndx = rnd.getInt(0, (int)(m_sharedNativePixmaps.size()-1)); |
| |
| pixmaps.push_back(m_sharedNativePixmaps[ndx]); |
| m_sharedNativePixmaps.erase(m_sharedNativePixmaps.begin() + ndx); |
| } |
| |
| for (int windowNdx = 0; windowNdx < windowCount; windowNdx++) |
| { |
| const int ndx = rnd.getInt(0, (int)(m_sharedNativeWindows.size()-1)); |
| |
| windows.push_back(m_sharedNativeWindows[ndx]); |
| m_sharedNativeWindows.erase(m_sharedNativeWindows.begin() + ndx); |
| } |
| |
| for (int contextNdx = 0; contextNdx < contextCount; contextNdx++) |
| { |
| const int ndx = rnd.getInt(0, (int)(m_sharedContexts.size()-1)); |
| |
| contexts.push_back(m_sharedContexts[ndx]); |
| m_sharedContexts.erase(m_sharedContexts.begin() + ndx); |
| } |
| } |
| |
| void MultiThreadedObjectTest::querySetSharedObjects (TestThread& thread, int count) |
| { |
| const Library& egl = getLibrary(); |
| de::Random& rnd = (thread.getId() == 0 ? m_rnd0 : m_rnd1); |
| vector<Type> objectTypes; |
| |
| if ((m_types & TYPE_PBUFFER) != 0) |
| objectTypes.push_back(TYPE_PBUFFER); |
| |
| if ((m_types & TYPE_PIXMAP) != 0) |
| objectTypes.push_back(TYPE_PIXMAP); |
| |
| if (!m_sharedNativeWindows.empty() && (m_types & TYPE_WINDOW) != 0) |
| objectTypes.push_back(TYPE_WINDOW); |
| |
| if ((m_types & TYPE_CONTEXT) != 0) |
| objectTypes.push_back(TYPE_CONTEXT); |
| |
| for (int queryNdx = 0; queryNdx < count; queryNdx++) |
| { |
| const Type type = rnd.choose<Type>(objectTypes.begin(), objectTypes.end()); |
| EGLSurface surface = EGL_NO_SURFACE; |
| EGLContext context = EGL_NO_CONTEXT; |
| |
| switch (type) |
| { |
| case TYPE_PBUFFER: |
| surface = m_sharedPbuffers[rnd.getInt(0, (int)(m_sharedPbuffers.size()-1))]; |
| break; |
| |
| case TYPE_PIXMAP: |
| surface = m_sharedNativePixmaps[rnd.getInt(0, (int)(m_sharedNativePixmaps.size()-1))].second; |
| break; |
| |
| case TYPE_WINDOW: |
| surface = m_sharedNativeWindows[rnd.getInt(0, (int)(m_sharedNativeWindows.size()-1))].second; |
| break; |
| |
| case TYPE_CONTEXT: |
| context = m_sharedContexts[rnd.getInt(0, (int)(m_sharedContexts.size()-1))]; |
| break; |
| |
| default: |
| DE_ASSERT(false); |
| } |
| |
| if (surface != EGL_NO_SURFACE) |
| { |
| static const EGLint queryAttributes[] = |
| { |
| EGL_LARGEST_PBUFFER, |
| EGL_HEIGHT, |
| EGL_WIDTH |
| }; |
| |
| const EGLint attribute = queryAttributes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(queryAttributes) - 1)]; |
| EGLBoolean result; |
| EGLint value; |
| |
| result = egl.querySurface(m_display, surface, attribute, &value); |
| thread.getLog() << ThreadLog::BeginMessage << result << " = eglQuerySurface(" << m_display << ", " << surface << ", " << attribute << ", " << value << ")" << ThreadLog::EndMessage; |
| EGLU_CHECK_MSG(egl, "eglQuerySurface()"); |
| |
| } |
| else if (context != EGL_NO_CONTEXT) |
| { |
| static const EGLint attributes[] = |
| { |
| EGL_CONFIG_ID, |
| EGL_CONTEXT_CLIENT_TYPE, |
| EGL_CONTEXT_CLIENT_VERSION, |
| EGL_RENDER_BUFFER |
| }; |
| |
| const EGLint attribute = attributes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(attributes)-1)]; |
| EGLint value; |
| EGLBoolean result; |
| |
| result = egl.queryContext(m_display, context, attribute, &value); |
| thread.getLog() << ThreadLog::BeginMessage << result << " = eglQueryContext(" << m_display << ", " << context << ", " << attribute << ", " << value << ")" << ThreadLog::EndMessage; |
| EGLU_CHECK_MSG(egl, "eglQueryContext()"); |
| |
| } |
| else |
| DE_ASSERT(false); |
| } |
| } |
| |
| void MultiThreadedObjectTest::destroyObjects (TestThread& thread) |
| { |
| const Library& egl = getLibrary(); |
| vector<EGLSurface>& pbuffers = (thread.getId() == 0 ? m_pbuffers0 : m_pbuffers1); |
| vector<pair<eglu::NativeWindow*, EGLSurface> >& windows = (thread.getId() == 0 ? m_nativeWindows0 : m_nativeWindows1); |
| vector<pair<eglu::NativePixmap*, EGLSurface> >& pixmaps = (thread.getId() == 0 ? m_nativePixmaps0 : m_nativePixmaps1); |
| vector<EGLContext>& contexts = (thread.getId() == 0 ? m_contexts0 : m_contexts1); |
| |
| for (int pbufferNdx = 0; pbufferNdx < (int)pbuffers.size(); pbufferNdx++) |
| { |
| if (pbuffers[pbufferNdx] != EGL_NO_SURFACE) |
| { |
| // Destroy EGLSurface |
| EGLBoolean result; |
| |
| result = egl.destroySurface(m_display, pbuffers[pbufferNdx]); |
| thread.getLog() << ThreadLog::BeginMessage << result << " = eglDestroySurface(" << m_display << ", " << pbuffers[pbufferNdx] << ")" << ThreadLog::EndMessage; |
| EGLU_CHECK_MSG(egl, "eglDestroySurface()"); |
| pbuffers[pbufferNdx] = EGL_NO_SURFACE; |
| } |
| } |
| pbuffers.clear(); |
| |
| for (int windowNdx = 0; windowNdx < (int)windows.size(); windowNdx++) |
| { |
| if (windows[windowNdx].second != EGL_NO_SURFACE) |
| { |
| thread.getLog() << ThreadLog::BeginMessage << "eglDestroySurface(" << m_display << ", " << windows[windowNdx].second << ")" << ThreadLog::EndMessage; |
| EGLU_CHECK_CALL(egl, destroySurface(m_display, windows[windowNdx].second)); |
| windows[windowNdx].second = EGL_NO_SURFACE; |
| } |
| |
| if (windows[windowNdx].first) |
| { |
| delete windows[windowNdx].first; |
| windows[windowNdx].first = NULL; |
| } |
| } |
| windows.clear(); |
| |
| for (int pixmapNdx = 0; pixmapNdx < (int)pixmaps.size(); pixmapNdx++) |
| { |
| if (pixmaps[pixmapNdx].first != EGL_NO_SURFACE) |
| { |
| thread.getLog() << ThreadLog::BeginMessage << "eglDestroySurface(" << m_display << ", " << pixmaps[pixmapNdx].second << ")" << ThreadLog::EndMessage; |
| EGLU_CHECK_CALL(egl, destroySurface(m_display, pixmaps[pixmapNdx].second)); |
| pixmaps[pixmapNdx].second = EGL_NO_SURFACE; |
| } |
| |
| if (pixmaps[pixmapNdx].first) |
| { |
| delete pixmaps[pixmapNdx].first; |
| pixmaps[pixmapNdx].first = NULL; |
| } |
| } |
| pixmaps.clear(); |
| |
| for (int contextNdx = 0; contextNdx < (int)contexts.size(); contextNdx++) |
| { |
| if (contexts[contextNdx] != EGL_NO_CONTEXT) |
| { |
| EGLU_CHECK_CALL(egl, destroyContext(m_display, contexts[contextNdx])); |
| thread.getLog() << ThreadLog::BeginMessage << "eglDestroyContext(" << m_display << ", " << contexts[contextNdx] << ")" << ThreadLog::EndMessage; |
| contexts[contextNdx] = EGL_NO_CONTEXT; |
| } |
| } |
| contexts.clear(); |
| } |
| |
| MultiThreadedTests::MultiThreadedTests (EglTestContext& context) |
| : TestCaseGroup(context, "multithread", "Multithreaded EGL tests") |
| { |
| } |
| |
| void MultiThreadedTests::init (void) |
| { |
| // Config tests |
| addChild(new MultiThreadedConfigTest(m_eglTestCtx, "config", "", 30, 30, 30)); |
| |
| // Object tests |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "pbuffer", "", MultiThreadedObjectTest::TYPE_PBUFFER)); |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "pixmap", "", MultiThreadedObjectTest::TYPE_PIXMAP)); |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "window", "", MultiThreadedObjectTest::TYPE_WINDOW)); |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "single_window", "", MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_SINGLE_WINDOW)); |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "context", "", MultiThreadedObjectTest::TYPE_CONTEXT)); |
| |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "pbuffer_pixmap", "", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_PIXMAP)); |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "pbuffer_window", "", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_WINDOW)); |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "pbuffer_single_window", "", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_SINGLE_WINDOW)); |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "pbuffer_context", "", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_CONTEXT)); |
| |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "pixmap_window", "", MultiThreadedObjectTest::TYPE_PIXMAP|MultiThreadedObjectTest::TYPE_WINDOW)); |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "pixmap_single_window", "", MultiThreadedObjectTest::TYPE_PIXMAP|MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_SINGLE_WINDOW)); |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "pixmap_context", "", MultiThreadedObjectTest::TYPE_PIXMAP|MultiThreadedObjectTest::TYPE_CONTEXT)); |
| |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "window_context", "", MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_CONTEXT)); |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "single_window_context", "", MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_SINGLE_WINDOW|MultiThreadedObjectTest::TYPE_CONTEXT)); |
| |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "pbuffer_pixmap_window", "", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_PIXMAP|MultiThreadedObjectTest::TYPE_WINDOW)); |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "pbuffer_pixmap_single_window", "", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_PIXMAP|MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_SINGLE_WINDOW)); |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "pbuffer_pixmap_context", "", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_PIXMAP|MultiThreadedObjectTest::TYPE_CONTEXT)); |
| |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "pbuffer_window_context", "", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_CONTEXT)); |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "pbuffer_single_window_context", "", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_SINGLE_WINDOW|MultiThreadedObjectTest::TYPE_CONTEXT)); |
| |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "pixmap_window_context", "", MultiThreadedObjectTest::TYPE_PIXMAP|MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_CONTEXT)); |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "pixmap_single_window_context", "", MultiThreadedObjectTest::TYPE_PIXMAP|MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_SINGLE_WINDOW|MultiThreadedObjectTest::TYPE_CONTEXT)); |
| |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "pbuffer_pixmap_window_context", "", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_PIXMAP|MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_CONTEXT)); |
| addChild(new MultiThreadedObjectTest(m_eglTestCtx, "pbuffer_pixmap_single_window_context", "", MultiThreadedObjectTest::TYPE_PBUFFER|MultiThreadedObjectTest::TYPE_PIXMAP|MultiThreadedObjectTest::TYPE_WINDOW|MultiThreadedObjectTest::TYPE_SINGLE_WINDOW|MultiThreadedObjectTest::TYPE_CONTEXT)); |
| } |
| |
| } // egl |
| } // deqp |