1 /* 2 * Copyright 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define LOG_TAG "EGLCleanup" 18 #include <android/log.h> 19 #include <jni.h> 20 21 #include <EGL/egl.h> 22 #include <EGL/eglext.h> 23 24 #include <gtest/gtest.h> 25 26 #include <GLTestHelper.h> 27 28 #include <pthread.h> 29 30 #define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) 31 #define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 32 #define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) 33 #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 34 35 namespace android { 36 37 /** 38 * Tests EGL cleanup edge cases. 39 */ 40 class EGLCleanupTest : public ::testing::Test { 41 protected: 42 EGLCleanupTest() {} 43 44 virtual void SetUp() { 45 // Termination of a terminated display is defined to be a no-op. 46 // Android uses a refcounted implementation, so terminate it a few 47 // times to make sure it's really dead. Without this, we might not 48 // get all the way into the driver eglTerminate implementation 49 // when we call eglTerminate. 50 EGLDisplay disp = eglGetDisplay(EGL_DEFAULT_DISPLAY); 51 if (disp != EGL_NO_DISPLAY) { 52 ALOGD("speculative terminate"); 53 eglTerminate(disp); 54 eglTerminate(disp); 55 eglTerminate(disp); 56 } 57 } 58 virtual void TearDown() {} 59 }; 60 61 /** 62 * Perform an operation and then start a new thread. 63 * 64 * The trick here is that some code may be helpfully releasing storage in 65 * pthread_key destructors. Those run after the thread returns out of the 66 * initial function, but before the thread fully exits. We want them to 67 * run concurrently with the next thread's initialization so we can confirm 68 * that the specified behavior of eglTerminate vs. eglInitialize holds. 69 */ 70 class ChainedThread { 71 public: 72 enum TestType { 73 TEST_CORRECT, 74 TEST_NO_RELEASE_CURRENT 75 }; 76 77 ChainedThread(TestType testType) : mEglDisplay(EGL_NO_DISPLAY), 78 mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT), 79 mTestType(testType), mIteration(0), mResult(true) { 80 pthread_mutex_init(&mLock, NULL); 81 pthread_cond_init(&mCond, NULL); 82 } 83 ~ChainedThread() { 84 // could get fancy and clean up the mutex 85 } 86 87 /* start here */ 88 bool start() { 89 lock(); 90 bool result = startThread_l(); 91 unlock(); 92 return result; 93 } 94 95 /* waits until test is done; when finished, call getResult() */ 96 bool waitForEnd() { 97 lock(); 98 int err = pthread_cond_wait(&mCond, &mLock); 99 if (err != 0) { 100 ALOGW("pthread_cond_wait failed: %d", err); 101 } 102 unlock(); 103 return err == 0; 104 } 105 106 /* returns the result; true means success */ 107 bool getResult() { 108 return mResult; 109 } 110 111 private: 112 enum { MAX_ITERATIONS = 1000 }; 113 114 EGLDisplay mEglDisplay; 115 EGLSurface mEglSurface; 116 EGLContext mEglContext; 117 118 TestType mTestType; 119 int mIteration; 120 bool mResult; 121 pthread_mutex_t mLock; 122 pthread_cond_t mCond; 123 124 // Assertions set a flag in Java and return from the current method (which 125 // must be declared to return void). They do not throw a C++ exception. 126 // 127 // Because we're running in a separate thread, which is not attached to 128 // the VM, the assert macros don't help us much. We could attach to the 129 // VM (by linking to libdvm.so and calling a global function), but the 130 // assertions won't cause the test to stop, which makes them less valuable. 131 // 132 // So instead we just return a boolean out of functions that can fail. 133 134 /* call this to start the test */ 135 bool startThread_l() { 136 pthread_attr_t attr; 137 pthread_attr_init(&attr); 138 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 139 140 pthread_t newThread; 141 int err = pthread_create(&newThread, &attr, ChainedThread::func, 142 (void*) this); 143 return (err == 0); 144 } 145 146 /* thread entry point */ 147 static void* func(void* arg) { 148 ChainedThread* obj = static_cast<ChainedThread*>(arg); 149 obj->doWork(); 150 return NULL; 151 } 152 153 bool lock() { 154 int err = pthread_mutex_lock(&mLock); 155 if (err != 0) { 156 ALOGW("pthread_mutex_lock failed: %d", err); 157 } 158 return err == 0; 159 } 160 161 bool unlock() { 162 int err = pthread_mutex_unlock(&mLock); 163 if (err != 0) { 164 ALOGW("pthread_mutex_unlock failed: %d", err); 165 } 166 return err == 0; 167 } 168 169 /* main worker */ 170 void doWork() { 171 lock(); 172 173 if ((mIteration % 25) == 0) { 174 ALOGD("iteration %d\n", mIteration); 175 } 176 177 mIteration++; 178 bool result = runTest_l(); 179 if (!result) { 180 ALOGW("failed at iteration %d, stopping test", mIteration); 181 mResult = false; 182 mIteration = MAX_ITERATIONS; 183 } 184 185 if (mIteration < MAX_ITERATIONS) { 186 // still going, try to start the next one 187 if (!startThread_l()) { 188 ALOGW("Unable to start thread at iter=%d", mIteration); 189 mResult = false; 190 mIteration = MAX_ITERATIONS; 191 } 192 } 193 194 if (mIteration >= MAX_ITERATIONS) { 195 ALOGD("Test ending, signaling main thread"); 196 pthread_cond_signal(&mCond); 197 } 198 199 unlock(); 200 } 201 202 /* setup, use, release EGL */ 203 bool runTest_l() { 204 if (!eglSetup()) { 205 return false; 206 } 207 if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, 208 mEglContext)) 209 { 210 ALOGW("eglMakeCurrent failed: 0x%x", eglGetError()); 211 return false; 212 } 213 if (!eglRelease_l()) { 214 return false; 215 } 216 217 return true; 218 } 219 220 /* 221 * Sets up EGL. Creates a 1280x720 pbuffer, which is large enough to 222 * cause a rapid and highly visible memory leak if we fail to discard it. 223 */ 224 bool eglSetup() { 225 static const EGLint kConfigAttribs[] = { 226 EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, 227 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 228 EGL_RED_SIZE, 8, 229 EGL_GREEN_SIZE, 8, 230 EGL_BLUE_SIZE, 8, 231 EGL_NONE 232 }; 233 static const EGLint kContextAttribs[] = { 234 EGL_CONTEXT_CLIENT_VERSION, 2, 235 EGL_NONE 236 }; 237 static const EGLint kPbufferAttribs[] = { 238 EGL_WIDTH, 1280, 239 EGL_HEIGHT, 720, 240 EGL_NONE 241 }; 242 243 //usleep(25000); 244 245 mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); 246 if (mEglDisplay == EGL_NO_DISPLAY) { 247 ALOGW("eglGetDisplay failed: 0x%x", eglGetError()); 248 return false; 249 } 250 251 EGLint majorVersion, minorVersion; 252 if (!eglInitialize(mEglDisplay, &majorVersion, &minorVersion)) { 253 ALOGW("eglInitialize failed: 0x%x", eglGetError()); 254 return false; 255 } 256 257 EGLConfig eglConfig; 258 EGLint numConfigs = 0; 259 if (!eglChooseConfig(mEglDisplay, kConfigAttribs, &eglConfig, 260 1, &numConfigs)) { 261 ALOGW("eglChooseConfig failed: 0x%x", eglGetError()); 262 return false; 263 } 264 265 mEglSurface = eglCreatePbufferSurface(mEglDisplay, eglConfig, 266 kPbufferAttribs); 267 if (mEglSurface == EGL_NO_SURFACE) { 268 ALOGW("eglCreatePbufferSurface failed: 0x%x", eglGetError()); 269 return false; 270 } 271 272 mEglContext = eglCreateContext(mEglDisplay, eglConfig, EGL_NO_CONTEXT, 273 kContextAttribs); 274 if (mEglContext == EGL_NO_CONTEXT) { 275 ALOGW("eglCreateContext failed: 0x%x", eglGetError()); 276 return false; 277 } 278 279 return true; 280 } 281 282 /* 283 * Releases EGL. How we do that depends on the type of test we're 284 * running. 285 */ 286 bool eglRelease_l() { 287 if (mEglDisplay == EGL_NO_DISPLAY) { 288 ALOGW("No display to release"); 289 return false; 290 } 291 292 switch (mTestType) { 293 case TEST_CORRECT: 294 eglTerminate(mEglDisplay); 295 eglReleaseThread(); 296 break; 297 case TEST_NO_RELEASE_CURRENT: 298 eglDestroyContext(mEglDisplay, mEglContext); 299 eglDestroySurface(mEglDisplay, mEglSurface); 300 eglTerminate(mEglDisplay); 301 break; 302 default: 303 ALOGE("Unknown test type %d", mTestType); 304 break; 305 } 306 307 int err = eglGetError(); 308 if (err != EGL_SUCCESS) { 309 ALOGW("eglRelease failed: 0x%x", err); 310 return false; 311 } 312 return true; 313 } 314 }; 315 316 317 /* do things correctly */ 318 TEST_F(EGLCleanupTest, TestCorrect) { 319 ALOGI("Starting TEST_CORRECT"); 320 ChainedThread cht(ChainedThread::TEST_CORRECT); 321 322 // start initial thread 323 ASSERT_TRUE(cht.start()); 324 325 // wait for the end 326 cht.waitForEnd(); 327 bool result = cht.getResult(); 328 ASSERT_TRUE(result); 329 } 330 331 /* try it without un-currenting the surfaces and context 332 TEST _F(EGLCleanupTest, TestNoReleaseCurrent) { 333 ALOGI("Starting TEST_NO_RELEASE_CURRENT"); 334 ChainedThread cht(ChainedThread::TEST_NO_RELEASE_CURRENT); 335 336 // start initial thread 337 ASSERT_TRUE(cht.start()); 338 339 // wait for the end 340 cht.waitForEnd(); 341 bool result = cht.getResult(); 342 ASSERT_TRUE(result); 343 } 344 */ 345 346 } // namespace android 347