Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2016 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 android.vr.cts;
     17 
     18 import android.app.Activity;
     19 import android.content.Context;
     20 import android.content.pm.PackageManager;
     21 import android.opengl.EGL14;
     22 import android.opengl.GLES20;
     23 import android.opengl.GLSurfaceView;
     24 import android.opengl.GLSurfaceView.Renderer;
     25 import android.os.Bundle;
     26 import android.util.Log;
     27 import android.view.Window;
     28 import android.view.WindowManager;
     29 
     30 import java.lang.InterruptedException;
     31 import java.util.concurrent.CountDownLatch;
     32 import java.util.concurrent.TimeUnit;
     33 
     34 import javax.microedition.khronos.egl.EGL10;
     35 import javax.microedition.khronos.egl.EGLConfig;
     36 import javax.microedition.khronos.egl.EGLContext;
     37 import javax.microedition.khronos.egl.EGLDisplay;
     38 import javax.microedition.khronos.egl.EGLSurface;
     39 
     40 public class OpenGLESActivity extends Activity {
     41     private static final String TAG = "OpenGLESActivity";
     42 
     43     public static final String EXTRA_VIEW_INDEX = "viewIndex";
     44     public static final String EXTRA_PROTECTED = "protected";
     45     public static final String EXTRA_PRIORITY = "priority";
     46     public static final String EXTRA_MUTABLE = "mutable";
     47     public static final String EXTRA_LATCH_COUNT = "latchCount";
     48 
     49 
     50     public static final int EGL_PROTECTED_CONTENT_EXT = 0x32C0;
     51 
     52     // EGL extension enums are not exposed in Java.
     53     public static final int EGL_CONTEXT_PRIORITY_LEVEL_IMG = 0x3100;
     54     public static final int EGL_MUTABLE_RENDER_BUFFER_BIT = 0x1000;
     55     private static final int EGL_OPENGL_ES3_BIT_KHR = 0x40;
     56 
     57     public static final int RENDERER_BASIC = 1;
     58     public static final int RENDERER_PROTECTEDTEXTURES = 2;
     59     public static final int RENDERER_REFRESHRATE = 3;
     60 
     61     OpenGLES20View mView;
     62     Renderer mRenderer;
     63     int mRendererType;
     64     private CountDownLatch mLatch;
     65 
     66     @Override
     67     public void onCreate(Bundle savedInstanceState) {
     68         super.onCreate(savedInstanceState);
     69         Window window = getWindow();
     70         window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
     71 
     72         int viewIndex = getIntent().getIntExtra(EXTRA_VIEW_INDEX, -1);
     73         int protectedAttribute = getIntent().getIntExtra(EXTRA_PROTECTED, -1);
     74         int priorityAttribute = getIntent().getIntExtra(EXTRA_PRIORITY, -1);
     75         int mutableAttribute =  getIntent().getIntExtra(EXTRA_MUTABLE, 0);
     76         int latchCount = getIntent().getIntExtra(EXTRA_LATCH_COUNT, 1);
     77         mLatch = new CountDownLatch(latchCount);
     78         mView = new OpenGLES20View(this, viewIndex, protectedAttribute, priorityAttribute,
     79             mutableAttribute, mLatch);
     80 
     81         setContentView(mView);
     82     }
     83 
     84     public int glGetError() {
     85         return ((RendererBasicTest)mRenderer).mError;
     86     }
     87 
     88     public static void checkEglError(String msg) {
     89         boolean failed = false;
     90         int error;
     91         while ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) {
     92             Log.e(TAG, msg + ": EGL error: 0x" + Integer.toHexString(error));
     93             failed = true;
     94         }
     95         if (failed) {
     96             throw new RuntimeException("EGL error encountered (EGL error: 0x" +
     97                 Integer.toHexString(error) + ")");
     98         }
     99     }
    100 
    101     public void runOnGlThread(Runnable r) throws Throwable {
    102         CountDownLatch fence = new CountDownLatch(1);
    103         RunSignalAndCatch wrapper = new RunSignalAndCatch(r, fence);
    104 
    105         mView.queueEvent(wrapper);
    106         fence.await(5000, TimeUnit.MILLISECONDS);
    107         if (wrapper.error != null) {
    108             throw wrapper.error;
    109         }
    110     }
    111 
    112     public static boolean contextHasAttributeWithValue(int attribute, int value) {
    113         int[] values = new int[1];
    114         EGL14.eglQueryContext(EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY),
    115             EGL14.eglGetCurrentContext(), attribute, values, 0);
    116         checkEglError("eglQueryContext");
    117         return values[0] == value;
    118     }
    119 
    120     public static boolean surfaceHasAttributeWithValue(int attribute, int value) {
    121         int[] values = new int[1];
    122         EGL14.eglQuerySurface(EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY),
    123             EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW), attribute, values, 0);
    124         checkEglError("eglQuerySurface");
    125         return values[0] == value;
    126     }
    127 
    128     public static void setSurfaceAttribute(int attribute, int value) {
    129         int[] values = new int[1];
    130         EGL14.eglSurfaceAttrib(EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY),
    131             EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW), attribute, value);
    132         checkEglError("eglSurfaceAttrib");
    133     }
    134 
    135     public boolean waitForFrameDrawn() {
    136         boolean result = false;
    137         try {
    138             result = mLatch.await(2L, TimeUnit.SECONDS);
    139         } catch (InterruptedException e) {
    140             // Ignore the exception and return false below.
    141         }
    142         return result;
    143     }
    144 
    145     public boolean supportsVrHighPerformance() {
    146         PackageManager pm = getPackageManager();
    147         return pm.hasSystemFeature(PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE);
    148     }
    149 
    150     @Override
    151     protected void onPause() {
    152         super.onPause();
    153         if (mView != null) {
    154             mView.onPause();
    155         }
    156     }
    157 
    158     @Override
    159     protected void onResume() {
    160         super.onResume();
    161         if (mView != null) {
    162             mView.onResume();
    163         }
    164     }
    165 
    166     private class RunSignalAndCatch implements Runnable {
    167         public Throwable error;
    168         private Runnable mRunnable;
    169         private CountDownLatch mFence;
    170 
    171         RunSignalAndCatch(Runnable run, CountDownLatch fence) {
    172             mRunnable = run;
    173             mFence = fence;
    174         }
    175 
    176         @Override
    177         public void run() {
    178             try {
    179                 mRunnable.run();
    180             } catch (Throwable t) {
    181                 error = t;
    182             } finally {
    183                 mFence.countDown();
    184             }
    185         }
    186     }
    187 
    188     class OpenGLES20View extends GLSurfaceView {
    189 
    190         public OpenGLES20View(Context context, int renderer, int protectedAttribute,
    191             int priorityAttribute, int mutableAttribute, CountDownLatch latch) {
    192             super(context);
    193             setEGLContextClientVersion(2);
    194 
    195             if (protectedAttribute == 1) {
    196                 setEGLContextFactory(new ProtectedContextFactory());
    197                 setEGLWindowSurfaceFactory(new ProtectedWindowSurfaceFactory());
    198             } else if (priorityAttribute != 0) {
    199                 setEGLContextFactory(new PriorityContextFactory(priorityAttribute));
    200             } else if (mutableAttribute != 0 && supportsVrHighPerformance()) {
    201                 setEGLConfigChooser(new MutableEGLConfigChooser());
    202             }
    203 
    204 
    205             if (renderer == RENDERER_BASIC) {
    206                 mRenderer = new RendererBasicTest(latch);
    207             } else if (renderer == RENDERER_PROTECTEDTEXTURES) {
    208                 mRenderer = new RendererProtectedTexturesTest(latch);
    209             } else if (renderer == RENDERER_REFRESHRATE) {
    210                 mRenderer = new RendererRefreshRateTest(latch);
    211             } else {
    212                 throw new RuntimeException();
    213             }
    214             setRenderer(mRenderer);
    215         }
    216 
    217         @Override
    218         public void setEGLContextClientVersion(int version) {
    219             super.setEGLContextClientVersion(version);
    220         }
    221     }
    222 
    223     private class PriorityContextFactory implements GLSurfaceView.EGLContextFactory {
    224         private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
    225         private int mEGLContextClientVersion = 2;
    226 
    227         private int mPriority;
    228 
    229         PriorityContextFactory(int priorityAttribute) {
    230             super();
    231             mPriority = priorityAttribute;
    232         }
    233 
    234         public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
    235             boolean highPerf = supportsVrHighPerformance();
    236             int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
    237                 highPerf ? EGL_CONTEXT_PRIORITY_LEVEL_IMG : EGL10.EGL_NONE,
    238                 highPerf ? mPriority : EGL10.EGL_NONE, EGL10.EGL_NONE };
    239 
    240             EGLContext context = egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
    241                 attrib_list);
    242             if (context == EGL10.EGL_NO_CONTEXT) {
    243                 Log.e(TAG, "Error creating EGL context.");
    244             }
    245             checkEglError("eglCreateContext");
    246             return context;
    247         }
    248 
    249         public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {
    250             if (!egl.eglDestroyContext(display, context)) {
    251                 Log.e("DefaultContextFactory", "display:" + display + " context: " + context);
    252             }
    253           }
    254     }
    255 
    256     private class ProtectedContextFactory implements GLSurfaceView.EGLContextFactory {
    257         private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
    258         private int mEGLContextClientVersion = 2;
    259 
    260         public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
    261             boolean highPerf = supportsVrHighPerformance();
    262             int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
    263                 highPerf ? EGL_PROTECTED_CONTENT_EXT : EGL10.EGL_NONE,
    264                 highPerf ? EGL14.EGL_TRUE : EGL10.EGL_NONE, EGL10.EGL_NONE };
    265 
    266             EGLContext context = egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
    267                 attrib_list);
    268             if (context == EGL10.EGL_NO_CONTEXT) {
    269               Log.e(TAG, "Error creating EGL context.");
    270             }
    271             checkEglError("eglCreateContext");
    272             return context;
    273         }
    274 
    275         public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {
    276             if (!egl.eglDestroyContext(display, context)) {
    277               Log.e("DefaultContextFactory", "display:" + display + " context: " + context);
    278             }
    279           }
    280     }
    281 
    282     private class ProtectedWindowSurfaceFactory implements GLSurfaceView.EGLWindowSurfaceFactory {
    283 
    284         public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display,
    285                                               EGLConfig config, Object nativeWindow) {
    286           EGLSurface result = null;
    287           try {
    288               boolean highPerf = supportsVrHighPerformance();
    289               int[] attrib_list = { highPerf ? EGL_PROTECTED_CONTENT_EXT : EGL10.EGL_NONE,
    290                       highPerf ? EGL14.EGL_TRUE : EGL10.EGL_NONE, EGL10.EGL_NONE };
    291               result = egl.eglCreateWindowSurface(display, config, nativeWindow, attrib_list);
    292               checkEglError("eglCreateWindowSurface");
    293           } catch (IllegalArgumentException e) {
    294               Log.e(TAG, "eglCreateWindowSurface", e);
    295           }
    296           return result;
    297         }
    298 
    299         public void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface) {
    300             egl.eglDestroySurface(display, surface);
    301         }
    302     }
    303 
    304     private class MutableEGLConfigChooser implements GLSurfaceView.EGLConfigChooser {
    305         public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
    306             int[] configSpec = new int[] {
    307                 EGL10.EGL_RED_SIZE, 8,
    308                 EGL10.EGL_GREEN_SIZE, 8,
    309                 EGL10.EGL_BLUE_SIZE, 8,
    310                 EGL10.EGL_ALPHA_SIZE, 8,
    311                 EGL10.EGL_DEPTH_SIZE, 16,
    312                 EGL10.EGL_STENCIL_SIZE, 8,
    313                 EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
    314                 EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT | EGL_MUTABLE_RENDER_BUFFER_BIT,
    315                 EGL10.EGL_NONE
    316             };
    317 
    318             int[] num_config = new int[1];
    319             if (!egl.eglChooseConfig(display, configSpec, null, 0, num_config)) {
    320                 throw new IllegalArgumentException("eglChooseConfig failed");
    321             }
    322 
    323             int numConfigs = num_config[0];
    324             if (numConfigs <= 0) {
    325                 throw new IllegalArgumentException("No configs match configSpec");
    326             }
    327 
    328             EGLConfig[] configs = new EGLConfig[numConfigs];
    329             if (!egl.eglChooseConfig(display, configSpec, configs, numConfigs,
    330                     num_config)) {
    331                 throw new IllegalArgumentException("eglChooseConfig#2 failed");
    332             }
    333             EGLConfig config = chooseConfig(egl, display, configs);
    334             if (config == null) {
    335                 throw new IllegalArgumentException("No config chosen");
    336             }
    337             return config;
    338         }
    339         public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
    340                 EGLConfig[] configs) {
    341             for (EGLConfig config : configs) {
    342                 int d = findConfigAttrib(egl, display, config,
    343                         EGL10.EGL_DEPTH_SIZE, 0);
    344                 int s = findConfigAttrib(egl, display, config,
    345                         EGL10.EGL_STENCIL_SIZE, 0);
    346 
    347                 // We need at least mDepthSize and mStencilSize bits
    348                 if (d < 16 || s < 8)
    349                     continue;
    350 
    351                 // We want an *exact* match for red/green/blue/alpha
    352                 int r = findConfigAttrib(egl, display, config,
    353                         EGL10.EGL_RED_SIZE, 0);
    354                 int g = findConfigAttrib(egl, display, config,
    355                             EGL10.EGL_GREEN_SIZE, 0);
    356                 int b = findConfigAttrib(egl, display, config,
    357                             EGL10.EGL_BLUE_SIZE, 0);
    358                 int a = findConfigAttrib(egl, display, config,
    359                         EGL10.EGL_ALPHA_SIZE, 0);
    360 
    361                 int mask = findConfigAttrib(egl, display, config,
    362                         EGL10.EGL_SURFACE_TYPE, 0);
    363 
    364                 if (r == 8 && g == 8 && b == 8 && a == 8 &&
    365                         (mask & EGL_MUTABLE_RENDER_BUFFER_BIT) != 0)
    366                     return config;
    367             }
    368             return null;
    369         }
    370 
    371         private int findConfigAttrib(EGL10 egl, EGLDisplay display,
    372                 EGLConfig config, int attribute, int defaultValue) {
    373 
    374             int[] value = new int[1];
    375             if (egl.eglGetConfigAttrib(display, config, attribute, value)) {
    376                 return value[0];
    377             }
    378             return defaultValue;
    379         }
    380     }
    381 }
    382