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