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