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