1 /* 2 * Copyright 2015 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 package org.webrtc; 12 13 import android.annotation.TargetApi; 14 import android.graphics.SurfaceTexture; 15 import android.opengl.EGL14; 16 import android.opengl.EGLConfig; 17 import android.opengl.EGLContext; 18 import android.opengl.EGLDisplay; 19 import android.opengl.EGLExt; 20 import android.opengl.EGLSurface; 21 import android.os.Build; 22 import android.support.annotation.Nullable; 23 import android.view.Surface; 24 import org.webrtc.EglBase; 25 26 /** 27 * Holds EGL state and utility methods for handling an EGL14 EGLContext, an EGLDisplay, 28 * and an EGLSurface. 29 */ 30 @SuppressWarnings("ReferenceEquality") // We want to compare to EGL14 constants. 31 @TargetApi(18) 32 class EglBase14Impl implements EglBase14 { 33 private static final String TAG = "EglBase14Impl"; 34 private static final int EGLExt_SDK_VERSION = Build.VERSION_CODES.JELLY_BEAN_MR2; 35 private static final int CURRENT_SDK_VERSION = Build.VERSION.SDK_INT; 36 private EGLContext eglContext; 37 @Nullable private EGLConfig eglConfig; 38 private EGLDisplay eglDisplay; 39 private EGLSurface eglSurface = EGL14.EGL_NO_SURFACE; 40 41 // EGL 1.4 is supported from API 17. But EGLExt that is used for setting presentation 42 // time stamp on a surface is supported from 18 so we require 18. isEGL14Supported()43 public static boolean isEGL14Supported() { 44 Logging.d(TAG, 45 "SDK version: " + CURRENT_SDK_VERSION 46 + ". isEGL14Supported: " + (CURRENT_SDK_VERSION >= EGLExt_SDK_VERSION)); 47 return (CURRENT_SDK_VERSION >= EGLExt_SDK_VERSION); 48 } 49 50 public static class Context implements EglBase14.Context { 51 private final EGLContext egl14Context; 52 53 @Override getRawContext()54 public EGLContext getRawContext() { 55 return egl14Context; 56 } 57 58 @Override 59 @SuppressWarnings("deprecation") 60 @TargetApi(Build.VERSION_CODES.LOLLIPOP) getNativeEglContext()61 public long getNativeEglContext() { 62 return CURRENT_SDK_VERSION >= Build.VERSION_CODES.LOLLIPOP ? egl14Context.getNativeHandle() 63 : egl14Context.getHandle(); 64 } 65 Context(android.opengl.EGLContext eglContext)66 public Context(android.opengl.EGLContext eglContext) { 67 this.egl14Context = eglContext; 68 } 69 } 70 71 // Create a new context with the specified config type, sharing data with sharedContext. 72 // |sharedContext| may be null. EglBase14Impl(EGLContext sharedContext, int[] configAttributes)73 public EglBase14Impl(EGLContext sharedContext, int[] configAttributes) { 74 eglDisplay = getEglDisplay(); 75 eglConfig = getEglConfig(eglDisplay, configAttributes); 76 final int openGlesVersion = EglBase.getOpenGlesVersionFromConfig(configAttributes); 77 Logging.d(TAG, "Using OpenGL ES version " + openGlesVersion); 78 eglContext = createEglContext(sharedContext, eglDisplay, eglConfig, openGlesVersion); 79 } 80 81 // Create EGLSurface from the Android Surface. 82 @Override createSurface(Surface surface)83 public void createSurface(Surface surface) { 84 createSurfaceInternal(surface); 85 } 86 87 // Create EGLSurface from the Android SurfaceTexture. 88 @Override createSurface(SurfaceTexture surfaceTexture)89 public void createSurface(SurfaceTexture surfaceTexture) { 90 createSurfaceInternal(surfaceTexture); 91 } 92 93 // Create EGLSurface from either Surface or SurfaceTexture. createSurfaceInternal(Object surface)94 private void createSurfaceInternal(Object surface) { 95 if (!(surface instanceof Surface) && !(surface instanceof SurfaceTexture)) { 96 throw new IllegalStateException("Input must be either a Surface or SurfaceTexture"); 97 } 98 checkIsNotReleased(); 99 if (eglSurface != EGL14.EGL_NO_SURFACE) { 100 throw new RuntimeException("Already has an EGLSurface"); 101 } 102 int[] surfaceAttribs = {EGL14.EGL_NONE}; 103 eglSurface = EGL14.eglCreateWindowSurface(eglDisplay, eglConfig, surface, surfaceAttribs, 0); 104 if (eglSurface == EGL14.EGL_NO_SURFACE) { 105 throw new RuntimeException( 106 "Failed to create window surface: 0x" + Integer.toHexString(EGL14.eglGetError())); 107 } 108 } 109 110 @Override createDummyPbufferSurface()111 public void createDummyPbufferSurface() { 112 createPbufferSurface(1, 1); 113 } 114 115 @Override createPbufferSurface(int width, int height)116 public void createPbufferSurface(int width, int height) { 117 checkIsNotReleased(); 118 if (eglSurface != EGL14.EGL_NO_SURFACE) { 119 throw new RuntimeException("Already has an EGLSurface"); 120 } 121 int[] surfaceAttribs = {EGL14.EGL_WIDTH, width, EGL14.EGL_HEIGHT, height, EGL14.EGL_NONE}; 122 eglSurface = EGL14.eglCreatePbufferSurface(eglDisplay, eglConfig, surfaceAttribs, 0); 123 if (eglSurface == EGL14.EGL_NO_SURFACE) { 124 throw new RuntimeException("Failed to create pixel buffer surface with size " + width + "x" 125 + height + ": 0x" + Integer.toHexString(EGL14.eglGetError())); 126 } 127 } 128 129 @Override getEglBaseContext()130 public Context getEglBaseContext() { 131 return new Context(eglContext); 132 } 133 134 @Override hasSurface()135 public boolean hasSurface() { 136 return eglSurface != EGL14.EGL_NO_SURFACE; 137 } 138 139 @Override surfaceWidth()140 public int surfaceWidth() { 141 final int widthArray[] = new int[1]; 142 EGL14.eglQuerySurface(eglDisplay, eglSurface, EGL14.EGL_WIDTH, widthArray, 0); 143 return widthArray[0]; 144 } 145 146 @Override surfaceHeight()147 public int surfaceHeight() { 148 final int heightArray[] = new int[1]; 149 EGL14.eglQuerySurface(eglDisplay, eglSurface, EGL14.EGL_HEIGHT, heightArray, 0); 150 return heightArray[0]; 151 } 152 153 @Override releaseSurface()154 public void releaseSurface() { 155 if (eglSurface != EGL14.EGL_NO_SURFACE) { 156 EGL14.eglDestroySurface(eglDisplay, eglSurface); 157 eglSurface = EGL14.EGL_NO_SURFACE; 158 } 159 } 160 checkIsNotReleased()161 private void checkIsNotReleased() { 162 if (eglDisplay == EGL14.EGL_NO_DISPLAY || eglContext == EGL14.EGL_NO_CONTEXT 163 || eglConfig == null) { 164 throw new RuntimeException("This object has been released"); 165 } 166 } 167 168 @Override release()169 public void release() { 170 checkIsNotReleased(); 171 releaseSurface(); 172 detachCurrent(); 173 synchronized (EglBase.lock) { 174 EGL14.eglDestroyContext(eglDisplay, eglContext); 175 } 176 EGL14.eglReleaseThread(); 177 EGL14.eglTerminate(eglDisplay); 178 eglContext = EGL14.EGL_NO_CONTEXT; 179 eglDisplay = EGL14.EGL_NO_DISPLAY; 180 eglConfig = null; 181 } 182 183 @Override makeCurrent()184 public void makeCurrent() { 185 checkIsNotReleased(); 186 if (eglSurface == EGL14.EGL_NO_SURFACE) { 187 throw new RuntimeException("No EGLSurface - can't make current"); 188 } 189 synchronized (EglBase.lock) { 190 if (!EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) { 191 throw new RuntimeException( 192 "eglMakeCurrent failed: 0x" + Integer.toHexString(EGL14.eglGetError())); 193 } 194 } 195 } 196 197 // Detach the current EGL context, so that it can be made current on another thread. 198 @Override detachCurrent()199 public void detachCurrent() { 200 synchronized (EglBase.lock) { 201 if (!EGL14.eglMakeCurrent( 202 eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT)) { 203 throw new RuntimeException( 204 "eglDetachCurrent failed: 0x" + Integer.toHexString(EGL14.eglGetError())); 205 } 206 } 207 } 208 209 @Override swapBuffers()210 public void swapBuffers() { 211 checkIsNotReleased(); 212 if (eglSurface == EGL14.EGL_NO_SURFACE) { 213 throw new RuntimeException("No EGLSurface - can't swap buffers"); 214 } 215 synchronized (EglBase.lock) { 216 EGL14.eglSwapBuffers(eglDisplay, eglSurface); 217 } 218 } 219 220 @Override swapBuffers(long timeStampNs)221 public void swapBuffers(long timeStampNs) { 222 checkIsNotReleased(); 223 if (eglSurface == EGL14.EGL_NO_SURFACE) { 224 throw new RuntimeException("No EGLSurface - can't swap buffers"); 225 } 226 synchronized (EglBase.lock) { 227 // See 228 // https://android.googlesource.com/platform/frameworks/native/+/tools_r22.2/opengl/specs/EGL_ANDROID_presentation_time.txt 229 EGLExt.eglPresentationTimeANDROID(eglDisplay, eglSurface, timeStampNs); 230 EGL14.eglSwapBuffers(eglDisplay, eglSurface); 231 } 232 } 233 234 // Return an EGLDisplay, or die trying. getEglDisplay()235 private static EGLDisplay getEglDisplay() { 236 EGLDisplay eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); 237 if (eglDisplay == EGL14.EGL_NO_DISPLAY) { 238 throw new RuntimeException( 239 "Unable to get EGL14 display: 0x" + Integer.toHexString(EGL14.eglGetError())); 240 } 241 int[] version = new int[2]; 242 if (!EGL14.eglInitialize(eglDisplay, version, 0, version, 1)) { 243 throw new RuntimeException( 244 "Unable to initialize EGL14: 0x" + Integer.toHexString(EGL14.eglGetError())); 245 } 246 return eglDisplay; 247 } 248 249 // Return an EGLConfig, or die trying. getEglConfig(EGLDisplay eglDisplay, int[] configAttributes)250 private static EGLConfig getEglConfig(EGLDisplay eglDisplay, int[] configAttributes) { 251 EGLConfig[] configs = new EGLConfig[1]; 252 int[] numConfigs = new int[1]; 253 if (!EGL14.eglChooseConfig( 254 eglDisplay, configAttributes, 0, configs, 0, configs.length, numConfigs, 0)) { 255 throw new RuntimeException( 256 "eglChooseConfig failed: 0x" + Integer.toHexString(EGL14.eglGetError())); 257 } 258 if (numConfigs[0] <= 0) { 259 throw new RuntimeException("Unable to find any matching EGL config"); 260 } 261 final EGLConfig eglConfig = configs[0]; 262 if (eglConfig == null) { 263 throw new RuntimeException("eglChooseConfig returned null"); 264 } 265 return eglConfig; 266 } 267 268 // Return an EGLConfig, or die trying. createEglContext(@ullable EGLContext sharedContext, EGLDisplay eglDisplay, EGLConfig eglConfig, int openGlesVersion)269 private static EGLContext createEglContext(@Nullable EGLContext sharedContext, 270 EGLDisplay eglDisplay, EGLConfig eglConfig, int openGlesVersion) { 271 if (sharedContext != null && sharedContext == EGL14.EGL_NO_CONTEXT) { 272 throw new RuntimeException("Invalid sharedContext"); 273 } 274 int[] contextAttributes = {EGL14.EGL_CONTEXT_CLIENT_VERSION, openGlesVersion, EGL14.EGL_NONE}; 275 EGLContext rootContext = sharedContext == null ? EGL14.EGL_NO_CONTEXT : sharedContext; 276 final EGLContext eglContext; 277 synchronized (EglBase.lock) { 278 eglContext = EGL14.eglCreateContext(eglDisplay, eglConfig, rootContext, contextAttributes, 0); 279 } 280 if (eglContext == EGL14.EGL_NO_CONTEXT) { 281 throw new RuntimeException( 282 "Failed to create EGL context: 0x" + Integer.toHexString(EGL14.eglGetError())); 283 } 284 return eglContext; 285 } 286 } 287