1 /* 2 * Copyright (C) 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 package android.opengl.cts; 18 19 import android.opengl.EGL14; 20 import android.opengl.EGLConfig; 21 import android.opengl.EGLContext; 22 import android.opengl.EGLDisplay; 23 import android.opengl.EGLSurface; 24 import android.opengl.GLES10; 25 import android.opengl.GLES20; 26 import android.test.AndroidTestCase; 27 import android.util.Log; 28 29 import java.nio.ByteBuffer; 30 import java.nio.IntBuffer; 31 32 /** 33 * Test some aspects of the Java-language wrappers generated for OpenGL. 34 */ 35 public class WrapperTest extends AndroidTestCase { 36 private static final String TAG = "WrapperTest"; 37 38 private EGLDisplay mEGLDisplay; 39 private EGLContext mEGLContext; 40 private EGLSurface mEGLSurface; 41 42 43 /** 44 * Tests range-checking on glGetIntegerv in GLES 1.x. 45 */ 46 public void testGetIntegerv1() { 47 eglSetup(1, 1, 1); // GLES 1.x with 1x1 pbuffer 48 49 checkGlError("start"); 50 51 int[] countBuf = new int[1]; 52 GLES10.glGetIntegerv(GLES10.GL_NUM_COMPRESSED_TEXTURE_FORMATS, countBuf, 0); 53 checkGlError("glGetIntegerv(count)"); 54 55 int formatCount = countBuf[0]; 56 Log.d(TAG, "got count=" + formatCount); 57 58 // try with a buffer large enough to hold all results 59 GLES10.glGetIntegerv(GLES10.GL_COMPRESSED_TEXTURE_FORMATS, new int[formatCount], 0); 60 checkGlError("glGetIntegerv(full1)"); 61 62 // try with an exact-fit IntBuffer 63 ByteBuffer fullByteBuf = ByteBuffer.allocateDirect(4 * formatCount); 64 IntBuffer fullIntBuf = fullByteBuf.asIntBuffer(); 65 GLES10.glGetIntegerv(GLES10.GL_COMPRESSED_TEXTURE_FORMATS, fullIntBuf); 66 checkGlError("glGetIntegerv(full2)"); 67 68 // try with an oversized IntBuffer with an offset 69 final int OFFSET = 5; 70 ByteBuffer oversizeByteBuf = ByteBuffer.allocateDirect(4 * (formatCount+OFFSET)); 71 IntBuffer oversizeIntBuf = oversizeByteBuf.asIntBuffer(); 72 oversizeIntBuf.position(OFFSET); 73 GLES10.glGetIntegerv(GLES10.GL_COMPRESSED_TEXTURE_FORMATS, oversizeIntBuf); 74 checkGlError("glGetIntegerv(full3)"); 75 assertEquals(oversizeIntBuf.get(OFFSET), fullIntBuf.get(0)); 76 77 // retry with a buffer that's too small -- should throw 78 ByteBuffer partialByteBuf = ByteBuffer.allocateDirect(4 * (formatCount - 1)); 79 IntBuffer partialIntBuf = partialByteBuf.asIntBuffer(); 80 try { 81 GLES10.glGetIntegerv(GLES10.GL_COMPRESSED_TEXTURE_FORMATS, partialIntBuf); 82 checkGlError("glGetIntegerv(partial1)"); 83 throw new RuntimeException("buffer has overrun (intbuf)"); 84 } catch (IllegalArgumentException iae) { 85 // good 86 } 87 88 try { 89 GLES10.glGetIntegerv(GLES10.GL_COMPRESSED_TEXTURE_FORMATS, new int[formatCount-1], 0); 90 checkGlError("glGetIntegerv(partial2)"); 91 throw new RuntimeException("buffer has overrun (int[])"); 92 } catch (IllegalArgumentException iae) { 93 // good 94 } 95 96 eglRelease(); 97 } 98 99 /** 100 * Tests range-checking on glGetIntegerv in GLES 2.x. 101 */ 102 public void testGetIntegerv2() { 103 eglSetup(2, 1, 1); // GLES 2.x with 1x1 pbuffer 104 105 checkGlError("start"); 106 107 int[] countBuf = new int[1]; 108 GLES20.glGetIntegerv(GLES20.GL_NUM_COMPRESSED_TEXTURE_FORMATS, countBuf, 0); 109 checkGlError("glGetIntegerv(count)"); 110 111 int formatCount = countBuf[0]; 112 Log.d(TAG, "got count=" + formatCount); 113 114 // try with a buffer large enough to hold all results 115 GLES20.glGetIntegerv(GLES20.GL_COMPRESSED_TEXTURE_FORMATS, new int[formatCount], 0); 116 checkGlError("glGetIntegerv(full1)"); 117 118 // try with an exact-fit IntBuffer 119 ByteBuffer fullByteBuf = ByteBuffer.allocateDirect(4 * formatCount); 120 IntBuffer fullIntBuf = fullByteBuf.asIntBuffer(); 121 GLES20.glGetIntegerv(GLES20.GL_COMPRESSED_TEXTURE_FORMATS, fullIntBuf); 122 checkGlError("glGetIntegerv(full2)"); 123 124 // try with an oversized IntBuffer with an offset 125 final int OFFSET = 5; 126 ByteBuffer oversizeByteBuf = ByteBuffer.allocateDirect(4 * (formatCount+OFFSET)); 127 IntBuffer oversizeIntBuf = oversizeByteBuf.asIntBuffer(); 128 oversizeIntBuf.position(OFFSET); 129 GLES20.glGetIntegerv(GLES20.GL_COMPRESSED_TEXTURE_FORMATS, oversizeIntBuf); 130 checkGlError("glGetIntegerv(full3)"); 131 assertEquals(oversizeIntBuf.get(OFFSET), fullIntBuf.get(0)); 132 133 // retry with a buffer that's too small -- should throw 134 ByteBuffer partialByteBuf = ByteBuffer.allocateDirect(4 * (formatCount - 1)); 135 IntBuffer partialIntBuf = partialByteBuf.asIntBuffer(); 136 try { 137 GLES20.glGetIntegerv(GLES20.GL_COMPRESSED_TEXTURE_FORMATS, partialIntBuf); 138 checkGlError("glGetIntegerv(partial1)"); 139 throw new RuntimeException("buffer has overrun (intbuf)"); 140 } catch (IllegalArgumentException iae) { 141 // good 142 } 143 144 try { 145 GLES20.glGetIntegerv(GLES20.GL_COMPRESSED_TEXTURE_FORMATS, new int[formatCount-1], 0); 146 checkGlError("glGetIntegerv(partial2)"); 147 throw new RuntimeException("buffer has overrun (int[])"); 148 } catch (IllegalArgumentException iae) { 149 // good 150 } 151 152 eglRelease(); 153 } 154 155 /** 156 * Tests whether EGL is releasing resources when the thread exits. If 157 * it doesn't, we'll consume memory rapidly, and will fail or be 158 * killed within a couple hundred iterations. 159 * <p> 160 * It may be worthwhile to watch the memory growth with procrank or showmap 161 * while the test runs to detect smaller leaks. 162 */ 163 public void testThreadCleanup() throws Throwable { 164 class WrappedTest implements Runnable { 165 public Throwable mThrowable; 166 167 private static final int WIDTH = 1280; 168 private static final int HEIGHT = 720; 169 170 @Override 171 public void run() { 172 try { 173 eglSetup(2, WIDTH, HEIGHT); 174 if (!EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) { 175 throw new RuntimeException("eglMakeCurrent failed"); 176 } 177 eglRelease(); 178 } catch (Throwable th) { 179 mThrowable = th; 180 } 181 } 182 } 183 184 WrappedTest wrappedTest = new WrappedTest(); 185 186 // Android has "reference-counted" EGL initialization. We want our eglTerminate call 187 // to be the "last" termination, since that's the situation we're trying to test, but 188 // it's possible that some previous test failed to balance eglInitialize and 189 // eglTerminate. So we call eglTerminate several times in a desperate attempt to 190 // zero out the refcount. 191 // 192 // Before we can terminate we need to be sure that the display has been initialized 193 // at least once. 194 eglSetup(2, 1, 1); 195 for (int i = 0; i < 100; i++) { 196 EGL14.eglTerminate(mEGLDisplay); 197 } 198 199 for (int i = 0; i < 1000; i++) { 200 if ((i % 25) == 0) { 201 Log.d(TAG, "iteration " + i); 202 } 203 204 Thread th = new Thread(wrappedTest, "EGL thrash"); 205 th.start(); 206 th.join(); 207 if (wrappedTest.mThrowable != null) { 208 throw wrappedTest.mThrowable; 209 } 210 } 211 } 212 213 /** 214 * Checks for GL errors. 215 */ 216 public void checkGlError(String op) { 217 int error; 218 while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { 219 Log.e(TAG, op + ": glError " + error); 220 throw new RuntimeException(op + ": glError " + error); 221 } 222 } 223 224 /** 225 * Prepares EGL. Pass in the desired GLES API version (1 or 2). 226 * <p> 227 * Sets mEGLDisplay, mEGLContext, and mEGLSurface, and makes them current. 228 */ 229 private void eglSetup(int api, int width, int height) { 230 mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); 231 if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) { 232 throw new RuntimeException("unable to get EGL14 display"); 233 } 234 int[] version = new int[2]; 235 if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) { 236 mEGLDisplay = null; 237 throw new RuntimeException("unable to initialize EGL14"); 238 } 239 240 int renderableType; 241 switch (api) { 242 case 1: 243 renderableType = EGL14.EGL_OPENGL_ES_BIT; 244 break; 245 case 2: 246 renderableType = EGL14.EGL_OPENGL_ES2_BIT; 247 break; 248 default: 249 throw new RuntimeException("unsupported API level " + api); 250 } 251 252 // Configure EGL for OpenGL ES 1.0 or 2.0, with a pbuffer 253 int[] attribList = { 254 EGL14.EGL_RED_SIZE, 8, 255 EGL14.EGL_GREEN_SIZE, 8, 256 EGL14.EGL_BLUE_SIZE, 8, 257 EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT, 258 EGL14.EGL_RENDERABLE_TYPE, renderableType, 259 EGL14.EGL_NONE 260 }; 261 EGLConfig[] configs = new EGLConfig[1]; 262 int[] numConfigs = new int[1]; 263 if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length, 264 numConfigs, 0)) { 265 throw new RuntimeException("unable to find RGB888+pbuffer ES" + api + " EGL config"); 266 } 267 268 // Create context 269 int[] attrib_list = { 270 EGL14.EGL_CONTEXT_CLIENT_VERSION, api, 271 EGL14.EGL_NONE 272 }; 273 mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT, 274 attrib_list, 0); 275 checkEglError("eglCreateContext"); 276 if (mEGLContext == null) { 277 throw new RuntimeException("null context"); 278 } 279 280 // Create a 1x1 pbuffer surface 281 int[] surfaceAttribs = { 282 EGL14.EGL_WIDTH, width, 283 EGL14.EGL_HEIGHT, height, 284 EGL14.EGL_NONE 285 }; 286 mEGLSurface = EGL14.eglCreatePbufferSurface(mEGLDisplay, configs[0], surfaceAttribs, 0); 287 checkEglError("eglCreatePbufferSurface"); 288 if (mEGLSurface == null) { 289 throw new RuntimeException("surface was null"); 290 } 291 292 // Make it current 293 if (!EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) { 294 throw new RuntimeException("eglMakeCurrent failed"); 295 } 296 } 297 298 /** 299 * Releases EGL goodies. 300 */ 301 private void eglRelease() { 302 // Terminating the display will release most objects, but won't discard the current 303 // surfaces and context until we release the thread. It shouldn't matter what order 304 // we do these in. 305 if (mEGLDisplay != null) { 306 EGL14.eglTerminate(mEGLDisplay); 307 EGL14.eglReleaseThread(); 308 } 309 310 // null everything out so future attempts to use this object will cause an NPE 311 mEGLDisplay = null; 312 mEGLContext = null; 313 mEGLSurface = null; 314 } 315 316 /** 317 * Checks for EGL errors. 318 */ 319 private void checkEglError(String msg) { 320 boolean failed = false; 321 int error; 322 while ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) { 323 Log.e(TAG, msg + ": EGL error: 0x" + Integer.toHexString(error)); 324 failed = true; 325 } 326 if (failed) { 327 throw new RuntimeException("EGL error encountered (see log)"); 328 } 329 } 330 } 331