Home | History | Annotate | Download | only in cts
      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