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
onCreate(Bundle savedInstanceState)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 
glGetError()84     public int glGetError() {
85         return ((RendererBasicTest)mRenderer).mError;
86     }
87 
checkEglError(String msg)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 
runOnGlThread(Runnable r)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 
contextHasAttributeWithValue(int attribute, int value)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 
surfaceHasAttributeWithValue(int attribute, int value)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 
setSurfaceAttribute(int attribute, int value)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 
waitForFrameDrawn()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 
supportsVrHighPerformance()145     public boolean supportsVrHighPerformance() {
146         PackageManager pm = getPackageManager();
147         return pm.hasSystemFeature(PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE);
148     }
149 
150     @Override
onPause()151     protected void onPause() {
152         super.onPause();
153         if (mView != null) {
154             mView.onPause();
155         }
156     }
157 
158     @Override
onResume()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 
RunSignalAndCatch(Runnable run, CountDownLatch fence)171         RunSignalAndCatch(Runnable run, CountDownLatch fence) {
172             mRunnable = run;
173             mFence = fence;
174         }
175 
176         @Override
run()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 
OpenGLES20View(Context context, int renderer, int protectedAttribute, int priorityAttribute, int mutableAttribute, CountDownLatch latch)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
setEGLContextClientVersion(int version)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 
PriorityContextFactory(int priorityAttribute)229         PriorityContextFactory(int priorityAttribute) {
230             super();
231             mPriority = priorityAttribute;
232         }
233 
createContext(EGL10 egl, EGLDisplay display, EGLConfig config)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 
destroyContext(EGL10 egl, EGLDisplay display, EGLContext context)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 
createContext(EGL10 egl, EGLDisplay display, EGLConfig config)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 
destroyContext(EGL10 egl, EGLDisplay display, EGLContext context)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 
createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, Object nativeWindow)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 
destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface)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 {
chooseConfig(EGL10 egl, EGLDisplay display)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         }
chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs)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 
findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config, int attribute, int defaultValue)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