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 so call eglSetup first.
    194         //
    195         // IMPORTANT NOTE: If a previous test in this test group fails to cleanup its
    196         // GLSurfaceView there may be a crash here on some platforms due to an object destruction
    197         // race condition. The solution is to make sure all previous tests override onPause()
    198         // and call their GLSurfaceView's onPause() function there. See b/37118199 for history.
    199         eglSetup(2, 1, 1);
    200         for (int i = 0; i < 100; i++) {
    201             EGL14.eglTerminate(mEGLDisplay);
    202         }
    203 
    204         for (int i = 0; i < 1000; i++) {
    205             if ((i % 25) == 0) {
    206                 Log.d(TAG, "iteration " + i);
    207             }
    208 
    209             Thread th = new Thread(wrappedTest, "EGL thrash");
    210             th.start();
    211             th.join();
    212             if (wrappedTest.mThrowable != null) {
    213                 throw wrappedTest.mThrowable;
    214             }
    215         }
    216     }
    217 
    218     /**
    219      * Checks for GL errors.
    220      */
    221     public void checkGlError(String op) {
    222         int error;
    223         while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
    224             Log.e(TAG, op + ": glError " + error);
    225             throw new RuntimeException(op + ": glError " + error);
    226         }
    227     }
    228 
    229     /**
    230      * Prepares EGL.  Pass in the desired GLES API version (1 or 2).
    231      * <p>
    232      * Sets mEGLDisplay, mEGLContext, and mEGLSurface, and makes them current.
    233      */
    234     private void eglSetup(int api, int width, int height) {
    235         mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
    236         if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
    237             throw new RuntimeException("unable to get EGL14 display");
    238         }
    239         int[] version = new int[2];
    240         if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
    241             mEGLDisplay = null;
    242             throw new RuntimeException("unable to initialize EGL14");
    243         }
    244 
    245         int renderableType;
    246         switch (api) {
    247             case 1:
    248                 renderableType = EGL14.EGL_OPENGL_ES_BIT;
    249                 break;
    250             case 2:
    251                 renderableType = EGL14.EGL_OPENGL_ES2_BIT;
    252                 break;
    253             default:
    254                 throw new RuntimeException("unsupported API level " + api);
    255         }
    256 
    257         // Configure EGL for OpenGL ES 1.0 or 2.0, with a pbuffer
    258         int[] attribList = {
    259                 EGL14.EGL_RED_SIZE, 8,
    260                 EGL14.EGL_GREEN_SIZE, 8,
    261                 EGL14.EGL_BLUE_SIZE, 8,
    262                 EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT,
    263                 EGL14.EGL_RENDERABLE_TYPE, renderableType,
    264                 EGL14.EGL_NONE
    265         };
    266         EGLConfig[] configs = new EGLConfig[1];
    267         int[] numConfigs = new int[1];
    268         if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length,
    269                 numConfigs, 0)) {
    270             throw new RuntimeException("unable to find RGB888+pbuffer ES" + api + " EGL config");
    271         }
    272 
    273         // Create context
    274         int[] attrib_list = {
    275                 EGL14.EGL_CONTEXT_CLIENT_VERSION, api,
    276                 EGL14.EGL_NONE
    277         };
    278         mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT,
    279                 attrib_list, 0);
    280         checkEglError("eglCreateContext");
    281         if (mEGLContext == null) {
    282             throw new RuntimeException("null context");
    283         }
    284 
    285         // Create a 1x1 pbuffer surface
    286         int[] surfaceAttribs = {
    287                 EGL14.EGL_WIDTH, width,
    288                 EGL14.EGL_HEIGHT, height,
    289                 EGL14.EGL_NONE
    290         };
    291         mEGLSurface = EGL14.eglCreatePbufferSurface(mEGLDisplay, configs[0], surfaceAttribs, 0);
    292         checkEglError("eglCreatePbufferSurface");
    293         if (mEGLSurface == null) {
    294             throw new RuntimeException("surface was null");
    295         }
    296 
    297         // Make it current
    298         if (!EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
    299             throw new RuntimeException("eglMakeCurrent failed");
    300         }
    301     }
    302 
    303     /**
    304      * Releases EGL goodies.
    305      */
    306     private void eglRelease() {
    307         // Terminating the display will release most objects, but won't discard the current
    308         // surfaces and context until we release the thread.  It shouldn't matter what order
    309         // we do these in.
    310         if (mEGLDisplay != null) {
    311             EGL14.eglTerminate(mEGLDisplay);
    312             EGL14.eglReleaseThread();
    313         }
    314 
    315         // null everything out so future attempts to use this object will cause an NPE
    316         mEGLDisplay = null;
    317         mEGLContext = null;
    318         mEGLSurface = null;
    319     }
    320 
    321     /**
    322      * Checks for EGL errors.
    323      */
    324     private void checkEglError(String msg) {
    325         boolean failed = false;
    326         int error;
    327         while ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) {
    328             Log.e(TAG, msg + ": EGL error: 0x" + Integer.toHexString(error));
    329             failed = true;
    330         }
    331         if (failed) {
    332             throw new RuntimeException("EGL error encountered (see log)");
    333         }
    334     }
    335 }
    336