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