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.graphics.Canvas; 14 import android.graphics.Rect; 15 import android.graphics.SurfaceTexture; 16 import androidx.annotation.Nullable; 17 import android.view.Surface; 18 import android.view.SurfaceHolder; 19 import javax.microedition.khronos.egl.EGL10; 20 import javax.microedition.khronos.egl.EGLConfig; 21 import javax.microedition.khronos.egl.EGLContext; 22 import javax.microedition.khronos.egl.EGLDisplay; 23 import javax.microedition.khronos.egl.EGLSurface; 24 25 /** 26 * Holds EGL state and utility methods for handling an egl 1.0 EGLContext, an EGLDisplay, 27 * and an EGLSurface. 28 */ 29 class EglBase10Impl implements EglBase10 { 30 private static final String TAG = "EglBase10Impl"; 31 // This constant is taken from EGL14.EGL_CONTEXT_CLIENT_VERSION. 32 private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; 33 34 private final EGL10 egl; 35 private EGLContext eglContext; 36 @Nullable private EGLConfig eglConfig; 37 private EGLDisplay eglDisplay; 38 private EGLSurface eglSurface = EGL10.EGL_NO_SURFACE; 39 40 // EGL wrapper for an actual EGLContext. 41 private static class Context implements EglBase10.Context { 42 private final EGLContext eglContext; 43 44 @Override getRawContext()45 public EGLContext getRawContext() { 46 return eglContext; 47 } 48 49 @Override getNativeEglContext()50 public long getNativeEglContext() { 51 // TODO(magjed): Implement. There is no easy way of getting the native context for EGL 1.0. We 52 // need to make sure to have an EglSurface, then make the context current using that surface, 53 // and then call into JNI and call the native version of eglGetCurrentContext. Then we need to 54 // restore the state and return the native context. 55 return 0 /* EGL_NO_CONTEXT */; 56 } 57 Context(EGLContext eglContext)58 public Context(EGLContext eglContext) { 59 this.eglContext = eglContext; 60 } 61 } 62 63 // Create a new context with the specified config type, sharing data with sharedContext. EglBase10Impl(EGLContext sharedContext, int[] configAttributes)64 public EglBase10Impl(EGLContext sharedContext, int[] configAttributes) { 65 this.egl = (EGL10) EGLContext.getEGL(); 66 eglDisplay = getEglDisplay(); 67 eglConfig = getEglConfig(eglDisplay, configAttributes); 68 final int openGlesVersion = EglBase.getOpenGlesVersionFromConfig(configAttributes); 69 Logging.d(TAG, "Using OpenGL ES version " + openGlesVersion); 70 eglContext = createEglContext(sharedContext, eglDisplay, eglConfig, openGlesVersion); 71 } 72 73 @Override createSurface(Surface surface)74 public void createSurface(Surface surface) { 75 /** 76 * We have to wrap Surface in a SurfaceHolder because for some reason eglCreateWindowSurface 77 * couldn't actually take a Surface object until API 17. Older versions fortunately just call 78 * SurfaceHolder.getSurface(), so we'll do that. No other methods are relevant. 79 */ 80 class FakeSurfaceHolder implements SurfaceHolder { 81 private final Surface surface; 82 83 FakeSurfaceHolder(Surface surface) { 84 this.surface = surface; 85 } 86 87 @Override 88 public void addCallback(Callback callback) {} 89 90 @Override 91 public void removeCallback(Callback callback) {} 92 93 @Override 94 public boolean isCreating() { 95 return false; 96 } 97 98 @Deprecated 99 @Override 100 public void setType(int i) {} 101 102 @Override 103 public void setFixedSize(int i, int i2) {} 104 105 @Override 106 public void setSizeFromLayout() {} 107 108 @Override 109 public void setFormat(int i) {} 110 111 @Override 112 public void setKeepScreenOn(boolean b) {} 113 114 @Nullable 115 @Override 116 public Canvas lockCanvas() { 117 return null; 118 } 119 120 @Nullable 121 @Override 122 public Canvas lockCanvas(Rect rect) { 123 return null; 124 } 125 126 @Override 127 public void unlockCanvasAndPost(Canvas canvas) {} 128 129 @Nullable 130 @Override 131 public Rect getSurfaceFrame() { 132 return null; 133 } 134 135 @Override 136 public Surface getSurface() { 137 return surface; 138 } 139 } 140 141 createSurfaceInternal(new FakeSurfaceHolder(surface)); 142 } 143 144 // Create EGLSurface from the Android SurfaceTexture. 145 @Override createSurface(SurfaceTexture surfaceTexture)146 public void createSurface(SurfaceTexture surfaceTexture) { 147 createSurfaceInternal(surfaceTexture); 148 } 149 150 // Create EGLSurface from either a SurfaceHolder or a SurfaceTexture. createSurfaceInternal(Object nativeWindow)151 private void createSurfaceInternal(Object nativeWindow) { 152 if (!(nativeWindow instanceof SurfaceHolder) && !(nativeWindow instanceof SurfaceTexture)) { 153 throw new IllegalStateException("Input must be either a SurfaceHolder or SurfaceTexture"); 154 } 155 checkIsNotReleased(); 156 if (eglSurface != EGL10.EGL_NO_SURFACE) { 157 throw new RuntimeException("Already has an EGLSurface"); 158 } 159 int[] surfaceAttribs = {EGL10.EGL_NONE}; 160 eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig, nativeWindow, surfaceAttribs); 161 if (eglSurface == EGL10.EGL_NO_SURFACE) { 162 throw new RuntimeException( 163 "Failed to create window surface: 0x" + Integer.toHexString(egl.eglGetError())); 164 } 165 } 166 167 // Create dummy 1x1 pixel buffer surface so the context can be made current. 168 @Override createDummyPbufferSurface()169 public void createDummyPbufferSurface() { 170 createPbufferSurface(1, 1); 171 } 172 173 @Override createPbufferSurface(int width, int height)174 public void createPbufferSurface(int width, int height) { 175 checkIsNotReleased(); 176 if (eglSurface != EGL10.EGL_NO_SURFACE) { 177 throw new RuntimeException("Already has an EGLSurface"); 178 } 179 int[] surfaceAttribs = {EGL10.EGL_WIDTH, width, EGL10.EGL_HEIGHT, height, EGL10.EGL_NONE}; 180 eglSurface = egl.eglCreatePbufferSurface(eglDisplay, eglConfig, surfaceAttribs); 181 if (eglSurface == EGL10.EGL_NO_SURFACE) { 182 throw new RuntimeException("Failed to create pixel buffer surface with size " + width + "x" 183 + height + ": 0x" + Integer.toHexString(egl.eglGetError())); 184 } 185 } 186 187 @Override getEglBaseContext()188 public org.webrtc.EglBase.Context getEglBaseContext() { 189 return new Context(eglContext); 190 } 191 192 @Override hasSurface()193 public boolean hasSurface() { 194 return eglSurface != EGL10.EGL_NO_SURFACE; 195 } 196 197 @Override surfaceWidth()198 public int surfaceWidth() { 199 final int widthArray[] = new int[1]; 200 egl.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_WIDTH, widthArray); 201 return widthArray[0]; 202 } 203 204 @Override surfaceHeight()205 public int surfaceHeight() { 206 final int heightArray[] = new int[1]; 207 egl.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_HEIGHT, heightArray); 208 return heightArray[0]; 209 } 210 211 @Override releaseSurface()212 public void releaseSurface() { 213 if (eglSurface != EGL10.EGL_NO_SURFACE) { 214 egl.eglDestroySurface(eglDisplay, eglSurface); 215 eglSurface = EGL10.EGL_NO_SURFACE; 216 } 217 } 218 checkIsNotReleased()219 private void checkIsNotReleased() { 220 if (eglDisplay == EGL10.EGL_NO_DISPLAY || eglContext == EGL10.EGL_NO_CONTEXT 221 || eglConfig == null) { 222 throw new RuntimeException("This object has been released"); 223 } 224 } 225 226 @Override release()227 public void release() { 228 checkIsNotReleased(); 229 releaseSurface(); 230 detachCurrent(); 231 egl.eglDestroyContext(eglDisplay, eglContext); 232 egl.eglTerminate(eglDisplay); 233 eglContext = EGL10.EGL_NO_CONTEXT; 234 eglDisplay = EGL10.EGL_NO_DISPLAY; 235 eglConfig = null; 236 } 237 238 @Override makeCurrent()239 public void makeCurrent() { 240 checkIsNotReleased(); 241 if (eglSurface == EGL10.EGL_NO_SURFACE) { 242 throw new RuntimeException("No EGLSurface - can't make current"); 243 } 244 synchronized (EglBase.lock) { 245 if (!egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) { 246 throw new RuntimeException( 247 "eglMakeCurrent failed: 0x" + Integer.toHexString(egl.eglGetError())); 248 } 249 } 250 } 251 252 // Detach the current EGL context, so that it can be made current on another thread. 253 @Override detachCurrent()254 public void detachCurrent() { 255 synchronized (EglBase.lock) { 256 if (!egl.eglMakeCurrent( 257 eglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT)) { 258 throw new RuntimeException( 259 "eglDetachCurrent failed: 0x" + Integer.toHexString(egl.eglGetError())); 260 } 261 } 262 } 263 264 @Override swapBuffers()265 public void swapBuffers() { 266 checkIsNotReleased(); 267 if (eglSurface == EGL10.EGL_NO_SURFACE) { 268 throw new RuntimeException("No EGLSurface - can't swap buffers"); 269 } 270 synchronized (EglBase.lock) { 271 egl.eglSwapBuffers(eglDisplay, eglSurface); 272 } 273 } 274 275 @Override swapBuffers(long timeStampNs)276 public void swapBuffers(long timeStampNs) { 277 // Setting presentation time is not supported for EGL 1.0. 278 swapBuffers(); 279 } 280 281 // Return an EGLDisplay, or die trying. getEglDisplay()282 private EGLDisplay getEglDisplay() { 283 EGLDisplay eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); 284 if (eglDisplay == EGL10.EGL_NO_DISPLAY) { 285 throw new RuntimeException( 286 "Unable to get EGL10 display: 0x" + Integer.toHexString(egl.eglGetError())); 287 } 288 int[] version = new int[2]; 289 if (!egl.eglInitialize(eglDisplay, version)) { 290 throw new RuntimeException( 291 "Unable to initialize EGL10: 0x" + Integer.toHexString(egl.eglGetError())); 292 } 293 return eglDisplay; 294 } 295 296 // Return an EGLConfig, or die trying. getEglConfig(EGLDisplay eglDisplay, int[] configAttributes)297 private EGLConfig getEglConfig(EGLDisplay eglDisplay, int[] configAttributes) { 298 EGLConfig[] configs = new EGLConfig[1]; 299 int[] numConfigs = new int[1]; 300 if (!egl.eglChooseConfig(eglDisplay, configAttributes, configs, configs.length, numConfigs)) { 301 throw new RuntimeException( 302 "eglChooseConfig failed: 0x" + Integer.toHexString(egl.eglGetError())); 303 } 304 if (numConfigs[0] <= 0) { 305 throw new RuntimeException("Unable to find any matching EGL config"); 306 } 307 final EGLConfig eglConfig = configs[0]; 308 if (eglConfig == null) { 309 throw new RuntimeException("eglChooseConfig returned null"); 310 } 311 return eglConfig; 312 } 313 314 // Return an EGLConfig, or die trying. createEglContext(@ullable EGLContext sharedContext, EGLDisplay eglDisplay, EGLConfig eglConfig, int openGlesVersion)315 private EGLContext createEglContext(@Nullable EGLContext sharedContext, EGLDisplay eglDisplay, 316 EGLConfig eglConfig, int openGlesVersion) { 317 if (sharedContext != null && sharedContext == EGL10.EGL_NO_CONTEXT) { 318 throw new RuntimeException("Invalid sharedContext"); 319 } 320 int[] contextAttributes = {EGL_CONTEXT_CLIENT_VERSION, openGlesVersion, EGL10.EGL_NONE}; 321 EGLContext rootContext = sharedContext == null ? EGL10.EGL_NO_CONTEXT : sharedContext; 322 final EGLContext eglContext; 323 synchronized (EglBase.lock) { 324 eglContext = egl.eglCreateContext(eglDisplay, eglConfig, rootContext, contextAttributes); 325 } 326 if (eglContext == EGL10.EGL_NO_CONTEXT) { 327 throw new RuntimeException( 328 "Failed to create EGL context: 0x" + Integer.toHexString(egl.eglGetError())); 329 } 330 return eglContext; 331 } 332 } 333