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