Home | History | Annotate | Download | only in deviceinfo
      1 /*
      2  * Copyright (C) 2015 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 package com.android.compatibility.common.deviceinfo;
     17 
     18 import android.app.Activity;
     19 import android.app.ActivityManager;
     20 import android.app.Instrumentation;
     21 import android.content.Context;
     22 import android.content.pm.ConfigurationInfo;
     23 import android.content.res.Configuration;
     24 import android.os.Bundle;
     25 import android.view.Window;
     26 import android.view.WindowManager;
     27 import android.opengl.EGL14;
     28 import android.opengl.EGLDisplay;
     29 import android.opengl.GLES20;
     30 import android.opengl.GLES30;
     31 import android.opengl.GLSurfaceView;
     32 import android.util.Log;
     33 
     34 import java.lang.reflect.Field;
     35 import java.nio.FloatBuffer;
     36 import java.util.ArrayList;
     37 import java.util.Arrays;
     38 import java.util.List;
     39 import java.util.Locale;
     40 import java.util.HashSet;
     41 import java.util.HashMap;
     42 import java.util.Scanner;
     43 import java.util.Set;
     44 import java.util.concurrent.CountDownLatch;
     45 
     46 import javax.microedition.khronos.egl.EGLConfig;
     47 import javax.microedition.khronos.opengles.GL10;
     48 
     49 /** Stub activity to collect data from the GlesView */
     50 public final class GlesStubActivity extends Activity {
     51 
     52     private static final String LOG_TAG = "GlesStubActivity";
     53     private int mVersion = -1;
     54     private GraphicsDeviceInfo mGraphicsDeviceInfo;
     55     private CountDownLatch mDone = new CountDownLatch(1);
     56     private HashSet<String> mOpenGlExtensions = new HashSet<>();
     57     private HashSet<String> mEglExtensions = new HashSet<>();
     58     private HashSet<String> mFormats = new HashSet<>();
     59     private HashMap<String, Object> mImplVariables = new HashMap<>();
     60     private HashSet<String> mDynamicArrayVariables = new HashSet<>();
     61     private String mGraphicsVendor;
     62     private String mGraphicsRenderer;
     63 
     64     @Override
     65     public void onCreate(Bundle bundle) {
     66         // Dismiss keyguard and keep screen on while this test is on.
     67         Window window = getWindow();
     68         window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
     69                 WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON |
     70                 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
     71 
     72         super.onCreate(bundle);
     73 
     74         window.setFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
     75                 WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
     76 
     77         ActivityManager activityManager =
     78                 (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
     79         ConfigurationInfo info = activityManager.getDeviceConfigurationInfo();
     80 
     81         mVersion = (info.reqGlEsVersion & 0xffff0000) >> 16;
     82 
     83         new Thread() {
     84             @Override
     85             public void run() {
     86                 runIterations(mVersion);
     87             }
     88         }.start();
     89     }
     90 
     91      /**
     92      * Wait for this activity to finish gathering information
     93      */
     94     public void waitForActivityToFinish() {
     95         try {
     96             mDone.await();
     97         } catch (InterruptedException e) {
     98             // just move on
     99         }
    100     }
    101 
    102     private void runIterations(int glVersion) {
    103         for (int i = 1; i <= glVersion; i++) {
    104             final CountDownLatch done = new CountDownLatch(1);
    105             final int version = i;
    106             GlesStubActivity.this.runOnUiThread(new Runnable() {
    107                 @Override
    108                 public void run() {
    109                     setContentView(new GlesSurfaceView(GlesStubActivity.this, version, done));
    110                 }
    111             });
    112             try {
    113                 done.await();
    114             } catch (InterruptedException e) {
    115                 // just move on
    116             }
    117         }
    118         mDone.countDown();
    119     }
    120 
    121     int getGlVersion() {
    122         return mVersion;
    123     }
    124 
    125     List<String> getOpenGlExtensions() {
    126         return new ArrayList<>(mOpenGlExtensions);
    127     }
    128 
    129     void addOpenGlExtension(String openGlExtension) {
    130         mOpenGlExtensions.add(openGlExtension);
    131     }
    132 
    133     List<String> getEglExtensions() {
    134         return new ArrayList<>(mEglExtensions);
    135     }
    136 
    137     void addEglExtensions(String[] eglExtensions) {
    138         // NOTE: We may end up here multiple times, using set to avoid dupes.
    139         mEglExtensions.addAll(Arrays.asList(eglExtensions));
    140     }
    141 
    142     List<String> getCompressedTextureFormats() {
    143         return new ArrayList<>(mFormats);
    144     }
    145 
    146     void addCompressedTextureFormat(String format) {
    147         mFormats.add(format);
    148     }
    149 
    150     String getVendor() {
    151         return mGraphicsVendor;
    152     }
    153 
    154     void setVendor(String vendor) {
    155         mGraphicsVendor = vendor;
    156     }
    157 
    158     String getRenderer() {
    159         return mGraphicsRenderer;
    160     }
    161 
    162     void setRenderer(String renderer) {
    163         mGraphicsRenderer = renderer;
    164     }
    165 
    166     public Set<String> getImplementationVariableNames() {
    167         return mImplVariables.keySet();
    168     }
    169 
    170     public Object getImplementationVariable(String name) {
    171         return mImplVariables.get(name);
    172     }
    173 
    174     public boolean isDynamicArrayVariable(String name) {
    175         return mDynamicArrayVariables.contains(name);
    176     }
    177 
    178     void addImplementationVariable(String name, Object value, boolean isDynamicArray) {
    179         mImplVariables.put(name, value);
    180         if (isDynamicArray) {
    181             mDynamicArrayVariables.add(name);
    182         }
    183     }
    184 
    185     static class GlesSurfaceView extends GLSurfaceView {
    186 
    187         public GlesSurfaceView(GlesStubActivity parent, int glVersion, CountDownLatch done) {
    188             super(parent);
    189 
    190             if (glVersion > 1) {
    191                 // Default is 1 so only set if bigger than 1
    192                 setEGLContextClientVersion(glVersion);
    193             }
    194             setRenderer(new OpenGlesRenderer(parent, glVersion, done));
    195         }
    196     }
    197 
    198     static abstract class ImplementationVariable {
    199         private Field mField;
    200         public ImplementationVariable(String fieldName) {
    201             try {
    202                 mField = GLES30.class.getField(fieldName);
    203             } catch (NoSuchFieldException e) {
    204                 Log.e(LOG_TAG, "Failed to get field reflection", e);
    205             }
    206         }
    207 
    208         public String getName() {
    209             return mField.getName();
    210         }
    211 
    212         public int getFieldIdValue() throws IllegalAccessException {
    213             return mField.getInt(null);
    214         }
    215 
    216         abstract public Object getValue();
    217 
    218         static protected int[] getIntValues(int fieldId, int count) throws IllegalAccessException{
    219             int[] resultInts = new int[count];
    220             // The JNI wrapper layer has a piece of code that defines
    221             // the expected array length. It defaults to 1 and looks
    222             // like it's missing GLES3 variables. So, we won't be
    223             // querying if the array has zero lenght.
    224             if (count > 0) {
    225                 GLES20.glGetIntegerv(fieldId, resultInts, 0);
    226             }
    227             return resultInts;
    228         }
    229     }
    230 
    231     static class IntVectorValue extends ImplementationVariable {
    232         private int mCount;
    233 
    234         public IntVectorValue(String fieldName, int count) {
    235             super(fieldName);
    236             mCount = count;
    237         }
    238 
    239         @Override
    240         public Object getValue() {
    241             Log.i(LOG_TAG, "Getting : " + this.getName() + " " + mCount);
    242             try {
    243                 return getIntValues(this.getFieldIdValue(), mCount);
    244             } catch (IllegalAccessException e) {
    245                 Log.e(LOG_TAG, "Failed to read the GL field", e);
    246             }
    247             return null;
    248         }
    249     }
    250 
    251     static class DynamicIntVectorValue extends ImplementationVariable {
    252         private Field mCountField;
    253 
    254         public DynamicIntVectorValue(String fieldName, String countFieldName) {
    255             super(fieldName);
    256             try {
    257                 mCountField = GLES30.class.getField(countFieldName);
    258             } catch (NoSuchFieldException e) {
    259                 Log.e(LOG_TAG, "Failed to get field reflection", e);
    260             }
    261         }
    262 
    263         @Override
    264         public Object getValue() {
    265             Log.i(LOG_TAG, "Getting : " + this.getName() + " " + mCountField.getName());
    266             try {
    267                 int[] count = new int[] {0};
    268                 GLES20.glGetIntegerv(mCountField.getInt(null), count, 0);
    269                 Log.i(LOG_TAG, "Getting : " + mCountField.getName() + " " + count[0]);
    270                 return getIntValues(this.getFieldIdValue(), count[0]);
    271             } catch (IllegalAccessException e) {
    272                 Log.e(LOG_TAG, "Failed to read the GL field", e);
    273             }
    274             return null;
    275         }
    276     }
    277 
    278     static class FloatVectorValue extends ImplementationVariable {
    279         private int mCount;
    280 
    281         public FloatVectorValue(String fieldName, int count) {
    282             super(fieldName);
    283             mCount = count;
    284         }
    285 
    286         @Override
    287         public Object getValue() {
    288             Log.i(LOG_TAG, "Getting : " + this.getName() + " " + mCount);
    289             try {
    290                 float[] result = new float[mCount];
    291                 GLES20.glGetFloatv(getFieldIdValue(), result, 0);
    292                 return result;
    293             } catch (IllegalAccessException e) {
    294                 Log.e(LOG_TAG, "Failed to read the GL field", e);
    295             }
    296             return null;
    297         }
    298     }
    299 
    300     static class LongVectorValue extends ImplementationVariable {
    301         private int mCount;
    302 
    303         public LongVectorValue(String fieldName, int count) {
    304             super(fieldName);
    305             mCount = count;
    306         }
    307 
    308         @Override
    309         public Object getValue() {
    310             Log.i(LOG_TAG, "Getting : " + this.getName() + " " + mCount);
    311             try {
    312                 long result[] = new long[mCount];
    313                 GLES30.glGetInteger64v(getFieldIdValue(), result, 0);
    314                 return result;
    315             } catch (IllegalAccessException e) {
    316                 Log.e(LOG_TAG, "Failed to read the GL field", e);
    317             }
    318             return null;
    319         }
    320     }
    321 
    322     static class StringValue extends ImplementationVariable {
    323         public StringValue(String fieldName) {
    324             super(fieldName);
    325         }
    326 
    327         @Override
    328         public Object getValue() {
    329             Log.i(LOG_TAG, "Getting : " + this.getName());
    330             String result = null;
    331             try {
    332                 result = GLES20.glGetString(this.getFieldIdValue());
    333             } catch (IllegalAccessException e) {
    334                 Log.e(LOG_TAG, "Failed to read the GL field", e);
    335             }
    336             return result;
    337         }
    338     }
    339 
    340     // NOTE: Changes to the types of the variables will carry over to
    341     // GraphicsDeviceInfo proto via GraphicsDeviceInfo. See
    342     // go/edi-userguide for details.
    343     static ImplementationVariable[] GLES2_IMPLEMENTATION_VARIABLES = {
    344         new IntVectorValue("GL_SUBPIXEL_BITS", 1),
    345         new IntVectorValue("GL_MAX_TEXTURE_SIZE", 1),
    346         new IntVectorValue("GL_MAX_CUBE_MAP_TEXTURE_SIZE", 1),
    347         new IntVectorValue("GL_MAX_VIEWPORT_DIMS", 2),
    348         new FloatVectorValue("GL_ALIASED_POINT_SIZE_RANGE", 2),
    349         new FloatVectorValue("GL_ALIASED_LINE_WIDTH_RANGE", 2),
    350         new DynamicIntVectorValue("GL_COMPRESSED_TEXTURE_FORMATS", "GL_NUM_COMPRESSED_TEXTURE_FORMATS"),
    351         new DynamicIntVectorValue("GL_SHADER_BINARY_FORMATS", "GL_NUM_SHADER_BINARY_FORMATS"),
    352         new IntVectorValue("GL_SHADER_COMPILER", 1),
    353         new StringValue("GL_SHADING_LANGUAGE_VERSION"),
    354         new StringValue("GL_VERSION"),
    355         new IntVectorValue("GL_MAX_VERTEX_ATTRIBS", 1),
    356         new IntVectorValue("GL_MAX_VERTEX_UNIFORM_VECTORS", 1),
    357         new IntVectorValue("GL_MAX_VARYING_VECTORS", 1),
    358         new IntVectorValue("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS", 1),
    359         new IntVectorValue("GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS", 1),
    360         new IntVectorValue("GL_MAX_TEXTURE_IMAGE_UNITS", 1),
    361         new IntVectorValue("GL_MAX_FRAGMENT_UNIFORM_VECTORS", 1),
    362         new IntVectorValue("GL_MAX_RENDERBUFFER_SIZE", 1)
    363     };
    364 
    365     static ImplementationVariable[] GLES3_IMPLEMENTATION_VARIABLES = {
    366         new LongVectorValue("GL_MAX_ELEMENT_INDEX", 1),
    367         new IntVectorValue("GL_MAX_3D_TEXTURE_SIZE", 1),
    368         new IntVectorValue("GL_MAX_ARRAY_TEXTURE_LAYERS", 1),
    369         new FloatVectorValue("GL_MAX_TEXTURE_LOD_BIAS", 1),
    370         new IntVectorValue("GL_MAX_DRAW_BUFFERS", 1),
    371         new IntVectorValue("GL_MAX_COLOR_ATTACHMENTS", 1),
    372         new IntVectorValue("GL_MAX_ELEMENTS_INDICES", 1),
    373         new IntVectorValue("GL_MAX_ELEMENTS_VERTICES", 1),
    374         new DynamicIntVectorValue("GL_PROGRAM_BINARY_FORMATS", "GL_NUM_PROGRAM_BINARY_FORMATS"),
    375         new LongVectorValue("GL_MAX_SERVER_WAIT_TIMEOUT", 1),
    376         new IntVectorValue("GL_MAJOR_VERSION", 1),
    377         new IntVectorValue("GL_MINOR_VERSION", 1),
    378         new IntVectorValue("GL_MAX_VERTEX_UNIFORM_COMPONENTS", 1),
    379         new IntVectorValue("GL_MAX_VERTEX_UNIFORM_BLOCKS", 1),
    380         new IntVectorValue("GL_MAX_VERTEX_OUTPUT_COMPONENTS", 1),
    381         new IntVectorValue("GL_MAX_FRAGMENT_UNIFORM_COMPONENTS", 1),
    382         new IntVectorValue("GL_MAX_FRAGMENT_UNIFORM_BLOCKS", 1),
    383         new IntVectorValue("GL_MAX_FRAGMENT_INPUT_COMPONENTS", 1),
    384         new IntVectorValue("GL_MIN_PROGRAM_TEXEL_OFFSET", 1),
    385         new IntVectorValue("GL_MAX_PROGRAM_TEXEL_OFFSET", 1),
    386         new IntVectorValue("GL_MAX_UNIFORM_BUFFER_BINDINGS", 1),
    387         new LongVectorValue("GL_MAX_UNIFORM_BLOCK_SIZE", 1),
    388         new IntVectorValue("GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT", 1),
    389         new IntVectorValue("GL_MAX_COMBINED_UNIFORM_BLOCKS", 1),
    390         new LongVectorValue("GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS", 1),
    391         new LongVectorValue("GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS", 1),
    392         new IntVectorValue("GL_MAX_VARYING_COMPONENTS", 1),
    393         new IntVectorValue("GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS", 1),
    394         new IntVectorValue("GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS", 1),
    395         new IntVectorValue("GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS", 1)
    396     };
    397 
    398     static class OpenGlesRenderer implements GLSurfaceView.Renderer {
    399 
    400         private final GlesStubActivity mParent;
    401         private final int mGlVersion;
    402         private final CountDownLatch mDone;
    403 
    404         OpenGlesRenderer(GlesStubActivity parent, int glVersion, CountDownLatch done) {
    405             mParent = parent;
    406             mGlVersion = glVersion;
    407             mDone = done;
    408         }
    409 
    410         @Override
    411         public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    412             String extensions;
    413             String vendor;
    414             String renderer;
    415             if (mGlVersion == 2) {
    416                 extensions = GLES20.glGetString(GLES20.GL_EXTENSIONS);
    417                 vendor = GLES20.glGetString(GLES20.GL_VENDOR);
    418                 renderer = GLES20.glGetString(GLES20.GL_RENDERER);
    419                 collectImplementationVariables(GLES2_IMPLEMENTATION_VARIABLES);
    420             } else if (mGlVersion == 3) {
    421                 extensions = GLES30.glGetString(GLES30.GL_EXTENSIONS);
    422                 vendor = GLES30.glGetString(GLES30.GL_VENDOR);
    423                 renderer = GLES30.glGetString(GLES30.GL_RENDERER);
    424                 collectImplementationVariables(GLES3_IMPLEMENTATION_VARIABLES);
    425             } else {
    426                 extensions = gl.glGetString(GL10.GL_EXTENSIONS);
    427                 vendor = gl.glGetString(GL10.GL_VENDOR);
    428                 renderer = gl.glGetString(GL10.GL_RENDERER);
    429             }
    430             mParent.setVendor(vendor);
    431             mParent.setRenderer(renderer);
    432             Scanner scanner = new Scanner(extensions);
    433             scanner.useDelimiter(" ");
    434             while (scanner.hasNext()) {
    435                 String ext = scanner.next();
    436                 mParent.addOpenGlExtension(ext);
    437                 if (ext.contains("texture")) {
    438                     if (ext.contains("compression") || ext.contains("compressed")) {
    439                         mParent.addCompressedTextureFormat(ext);
    440                     }
    441                 }
    442             }
    443             scanner.close();
    444 
    445             collectEglExtensions(mParent);
    446 
    447             mDone.countDown();
    448         }
    449 
    450         @Override
    451         public void onSurfaceChanged(GL10 gl, int width, int height) {}
    452 
    453         @Override
    454         public void onDrawFrame(GL10 gl) {}
    455 
    456         private void collectImplementationVariables(ImplementationVariable[] variables) {
    457             for (int i = 0; i < variables.length; i++) {
    458                 String name = variables[i].getName();
    459                 Object value = variables[i].getValue();
    460                 boolean dynamicArray = variables[i] instanceof DynamicIntVectorValue;
    461                 mParent.addImplementationVariable(name, value, dynamicArray);
    462             }
    463         }
    464 
    465         private static void collectEglExtensions(GlesStubActivity collector) {
    466             EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
    467             if (display == EGL14.EGL_NO_DISPLAY) {
    468                 Log.e(LOG_TAG, "Failed to init EGL default display: 0x" +
    469                         Integer.toHexString(EGL14.eglGetError()));
    470                 return;
    471             }
    472             String extensions = EGL14.eglQueryString(display, EGL14.EGL_EXTENSIONS);
    473             int error = EGL14.eglGetError();
    474             if (error != EGL14.EGL_SUCCESS) {
    475                 Log.e(LOG_TAG, "Failed to query extension string: 0x" + Integer.toHexString(error));
    476                 return;
    477             }
    478             // Fingers crossed for no extra white space in the extension string.
    479             collector.addEglExtensions(extensions.split(" "));
    480         }
    481     }
    482 }
    483