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
onCreate(Bundle bundle)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      */
waitForActivityToFinish()94     public void waitForActivityToFinish() {
95         try {
96             mDone.await();
97         } catch (InterruptedException e) {
98             // just move on
99         }
100     }
101 
runIterations(int glVersion)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 
getGlVersion()121     int getGlVersion() {
122         return mVersion;
123     }
124 
getOpenGlExtensions()125     List<String> getOpenGlExtensions() {
126         return new ArrayList<>(mOpenGlExtensions);
127     }
128 
addOpenGlExtension(String openGlExtension)129     void addOpenGlExtension(String openGlExtension) {
130         mOpenGlExtensions.add(openGlExtension);
131     }
132 
getEglExtensions()133     List<String> getEglExtensions() {
134         return new ArrayList<>(mEglExtensions);
135     }
136 
addEglExtensions(String[] eglExtensions)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 
getCompressedTextureFormats()142     List<String> getCompressedTextureFormats() {
143         return new ArrayList<>(mFormats);
144     }
145 
addCompressedTextureFormat(String format)146     void addCompressedTextureFormat(String format) {
147         mFormats.add(format);
148     }
149 
getVendor()150     String getVendor() {
151         return mGraphicsVendor;
152     }
153 
setVendor(String vendor)154     void setVendor(String vendor) {
155         mGraphicsVendor = vendor;
156     }
157 
getRenderer()158     String getRenderer() {
159         return mGraphicsRenderer;
160     }
161 
setRenderer(String renderer)162     void setRenderer(String renderer) {
163         mGraphicsRenderer = renderer;
164     }
165 
getImplementationVariableNames()166     public Set<String> getImplementationVariableNames() {
167         return mImplVariables.keySet();
168     }
169 
getImplementationVariable(String name)170     public Object getImplementationVariable(String name) {
171         return mImplVariables.get(name);
172     }
173 
isDynamicArrayVariable(String name)174     public boolean isDynamicArrayVariable(String name) {
175         return mDynamicArrayVariables.contains(name);
176     }
177 
addImplementationVariable(String name, Object value, boolean isDynamicArray)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 
GlesSurfaceView(GlesStubActivity parent, int glVersion, CountDownLatch done)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;
ImplementationVariable(String fieldName)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 
getName()208         public String getName() {
209             return mField.getName();
210         }
211 
getFieldIdValue()212         public int getFieldIdValue() throws IllegalAccessException {
213             return mField.getInt(null);
214         }
215 
getValue()216         abstract public Object getValue();
217 
getIntValues(int fieldId, int count)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 
IntVectorValue(String fieldName, int count)234         public IntVectorValue(String fieldName, int count) {
235             super(fieldName);
236             mCount = count;
237         }
238 
239         @Override
getValue()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 
DynamicIntVectorValue(String fieldName, String countFieldName)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
getValue()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 
FloatVectorValue(String fieldName, int count)281         public FloatVectorValue(String fieldName, int count) {
282             super(fieldName);
283             mCount = count;
284         }
285 
286         @Override
getValue()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 
LongVectorValue(String fieldName, int count)303         public LongVectorValue(String fieldName, int count) {
304             super(fieldName);
305             mCount = count;
306         }
307 
308         @Override
getValue()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 {
StringValue(String fieldName)323         public StringValue(String fieldName) {
324             super(fieldName);
325         }
326 
327         @Override
getValue()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 
OpenGlesRenderer(GlesStubActivity parent, int glVersion, CountDownLatch done)404         OpenGlesRenderer(GlesStubActivity parent, int glVersion, CountDownLatch done) {
405             mParent = parent;
406             mGlVersion = glVersion;
407             mDone = done;
408         }
409 
410         @Override
onSurfaceCreated(GL10 gl, EGLConfig config)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
onSurfaceChanged(GL10 gl, int width, int height)451         public void onSurfaceChanged(GL10 gl, int width, int height) {}
452 
453         @Override
onDrawFrame(GL10 gl)454         public void onDrawFrame(GL10 gl) {}
455 
collectImplementationVariables(ImplementationVariable[] variables)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 
collectEglExtensions(GlesStubActivity collector)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