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