• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 com.google.android.exoplayer2.util;
17 
18 import static android.opengl.GLU.gluErrorString;
19 
20 import android.content.Context;
21 import android.content.pm.PackageManager;
22 import android.opengl.EGL14;
23 import android.opengl.EGLConfig;
24 import android.opengl.EGLContext;
25 import android.opengl.EGLDisplay;
26 import android.opengl.EGLSurface;
27 import android.opengl.GLES11Ext;
28 import android.opengl.GLES20;
29 import androidx.annotation.DoNotInline;
30 import androidx.annotation.Nullable;
31 import androidx.annotation.RequiresApi;
32 import com.google.android.exoplayer2.C;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.nio.ByteBuffer;
36 import java.nio.ByteOrder;
37 import java.nio.FloatBuffer;
38 import javax.microedition.khronos.egl.EGL10;
39 
40 /** OpenGL ES utilities. */
41 @SuppressWarnings("InlinedApi") // GLES constants are used safely based on the API version.
42 public final class GlUtil {
43 
44   /** Thrown when an OpenGL error occurs and {@link #glAssertionsEnabled} is {@code true}. */
45   public static final class GlException extends RuntimeException {
46     /** Creates an instance with the specified error message. */
GlException(String message)47     public GlException(String message) {
48       super(message);
49     }
50   }
51 
52   /** Whether to throw a {@link GlException} in case of an OpenGL error. */
53   public static boolean glAssertionsEnabled = false;
54 
55   /** Number of vertices in a rectangle. */
56   public static final int RECTANGLE_VERTICES_COUNT = 4;
57 
58   private static final String TAG = "GlUtil";
59 
60   // https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_protected_content.txt
61   private static final String EXTENSION_PROTECTED_CONTENT = "EGL_EXT_protected_content";
62   // https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_surfaceless_context.txt
63   private static final String EXTENSION_SURFACELESS_CONTEXT = "EGL_KHR_surfaceless_context";
64 
65   // https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_gl_colorspace.txt
66   private static final int EGL_GL_COLORSPACE_KHR = 0x309D;
67   // https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_gl_colorspace_bt2020_linear.txt
68   private static final int EGL_GL_COLORSPACE_BT2020_PQ_EXT = 0x3340;
69 
70   private static final int[] EGL_WINDOW_SURFACE_ATTRIBUTES_NONE = new int[] {EGL14.EGL_NONE};
71   private static final int[] EGL_WINDOW_SURFACE_ATTRIBUTES_BT2020_PQ =
72       new int[] {EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_BT2020_PQ_EXT, EGL14.EGL_NONE};
73   private static final int[] EGL_CONFIG_ATTRIBUTES_RGBA_8888 =
74       new int[] {
75         EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
76         EGL14.EGL_RED_SIZE, /* redSize= */ 8,
77         EGL14.EGL_GREEN_SIZE, /* greenSize= */ 8,
78         EGL14.EGL_BLUE_SIZE, /* blueSize= */ 8,
79         EGL14.EGL_ALPHA_SIZE, /* alphaSize= */ 8,
80         EGL14.EGL_DEPTH_SIZE, /* depthSize= */ 0,
81         EGL14.EGL_STENCIL_SIZE, /* stencilSize= */ 0,
82         EGL14.EGL_NONE
83       };
84   private static final int[] EGL_CONFIG_ATTRIBUTES_RGBA_1010102 =
85       new int[] {
86         EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
87         EGL14.EGL_RED_SIZE, /* redSize= */ 10,
88         EGL14.EGL_GREEN_SIZE, /* greenSize= */ 10,
89         EGL14.EGL_BLUE_SIZE, /* blueSize= */ 10,
90         EGL14.EGL_ALPHA_SIZE, /* alphaSize= */ 2,
91         EGL14.EGL_DEPTH_SIZE, /* depthSize= */ 0,
92         EGL14.EGL_STENCIL_SIZE, /* stencilSize= */ 0,
93         EGL14.EGL_NONE
94       };
95 
96   /** Class only contains static methods. */
GlUtil()97   private GlUtil() {}
98 
99   /** Bounds of normalized device coordinates, commonly used for defining viewport boundaries. */
getNormalizedCoordinateBounds()100   public static float[] getNormalizedCoordinateBounds() {
101     return new float[] {
102       -1, -1, 0, 1,
103       1, -1, 0, 1,
104       -1, 1, 0, 1,
105       1, 1, 0, 1
106     };
107   }
108 
109   /** Typical bounds used for sampling from textures. */
getTextureCoordinateBounds()110   public static float[] getTextureCoordinateBounds() {
111     return new float[] {
112       0, 0, 0, 1,
113       1, 0, 0, 1,
114       0, 1, 0, 1,
115       1, 1, 0, 1
116     };
117   }
118 
119   /**
120    * Returns whether creating a GL context with {@value #EXTENSION_PROTECTED_CONTENT} is possible.
121    *
122    * <p>If {@code true}, the device supports a protected output path for DRM content when using GL.
123    */
isProtectedContentExtensionSupported(Context context)124   public static boolean isProtectedContentExtensionSupported(Context context) {
125     if (Util.SDK_INT < 24) {
126       return false;
127     }
128     if (Util.SDK_INT < 26 && ("samsung".equals(Util.MANUFACTURER) || "XT1650".equals(Util.MODEL))) {
129       // Samsung devices running Nougat are known to be broken. See
130       // https://github.com/google/ExoPlayer/issues/3373 and [Internal: b/37197802].
131       // Moto Z XT1650 is also affected. See
132       // https://github.com/google/ExoPlayer/issues/3215.
133       return false;
134     }
135     if (Util.SDK_INT < 26
136         && !context
137             .getPackageManager()
138             .hasSystemFeature(PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) {
139       // Pre API level 26 devices were not well tested unless they supported VR mode.
140       return false;
141     }
142 
143     EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
144     @Nullable String eglExtensions = EGL14.eglQueryString(display, EGL10.EGL_EXTENSIONS);
145     return eglExtensions != null && eglExtensions.contains(EXTENSION_PROTECTED_CONTENT);
146   }
147 
148   /**
149    * Returns whether the {@value #EXTENSION_SURFACELESS_CONTEXT} extension is supported.
150    *
151    * <p>This extension allows passing {@link EGL14#EGL_NO_SURFACE} for both the write and read
152    * surfaces in a call to {@link EGL14#eglMakeCurrent(EGLDisplay, EGLSurface, EGLSurface,
153    * EGLContext)}.
154    */
isSurfacelessContextExtensionSupported()155   public static boolean isSurfacelessContextExtensionSupported() {
156     if (Util.SDK_INT < 17) {
157       return false;
158     }
159     EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
160     @Nullable String eglExtensions = EGL14.eglQueryString(display, EGL10.EGL_EXTENSIONS);
161     return eglExtensions != null && eglExtensions.contains(EXTENSION_SURFACELESS_CONTEXT);
162   }
163 
164   /** Returns an initialized default {@link EGLDisplay}. */
165   @RequiresApi(17)
createEglDisplay()166   public static EGLDisplay createEglDisplay() {
167     return Api17.createEglDisplay();
168   }
169 
170   /** Returns a new {@link EGLContext} for the specified {@link EGLDisplay}. */
171   @RequiresApi(17)
createEglContext(EGLDisplay eglDisplay)172   public static EGLContext createEglContext(EGLDisplay eglDisplay) {
173     return Api17.createEglContext(eglDisplay, /* version= */ 2, EGL_CONFIG_ATTRIBUTES_RGBA_8888);
174   }
175 
176   /**
177    * Returns a new {@link EGLContext} for the specified {@link EGLDisplay}, requesting ES 3 and an
178    * RGBA 1010102 config.
179    */
180   @RequiresApi(17)
createEglContextEs3Rgba1010102(EGLDisplay eglDisplay)181   public static EGLContext createEglContextEs3Rgba1010102(EGLDisplay eglDisplay) {
182     return Api17.createEglContext(eglDisplay, /* version= */ 3, EGL_CONFIG_ATTRIBUTES_RGBA_1010102);
183   }
184 
185   /**
186    * Returns a new {@link EGLSurface} wrapping the specified {@code surface}.
187    *
188    * @param eglDisplay The {@link EGLDisplay} to attach the surface to.
189    * @param surface The surface to wrap; must be a surface, surface texture or surface holder.
190    */
191   @RequiresApi(17)
getEglSurface(EGLDisplay eglDisplay, Object surface)192   public static EGLSurface getEglSurface(EGLDisplay eglDisplay, Object surface) {
193     return Api17.getEglSurface(
194         eglDisplay, surface, EGL_CONFIG_ATTRIBUTES_RGBA_8888, EGL_WINDOW_SURFACE_ATTRIBUTES_NONE);
195   }
196 
197   /**
198    * Returns a new {@link EGLSurface} wrapping the specified {@code surface}, for HDR rendering with
199    * Rec. 2020 color primaries and using the PQ transfer function.
200    *
201    * @param eglDisplay The {@link EGLDisplay} to attach the surface to.
202    * @param surface The surface to wrap; must be a surface, surface texture or surface holder.
203    */
204   @RequiresApi(17)
getEglSurfaceBt2020Pq(EGLDisplay eglDisplay, Object surface)205   public static EGLSurface getEglSurfaceBt2020Pq(EGLDisplay eglDisplay, Object surface) {
206     return Api17.getEglSurface(
207         eglDisplay,
208         surface,
209         EGL_CONFIG_ATTRIBUTES_RGBA_1010102,
210         EGL_WINDOW_SURFACE_ATTRIBUTES_BT2020_PQ);
211   }
212 
213   /**
214    * Creates and focuses a new {@link EGLSurface} wrapping a 1x1 pixel buffer.
215    *
216    * @param eglContext The {@link EGLContext} to make current.
217    * @param eglDisplay The {@link EGLDisplay} to attach the surface to.
218    */
219   @RequiresApi(17)
focusPlaceholderEglSurface(EGLContext eglContext, EGLDisplay eglDisplay)220   public static void focusPlaceholderEglSurface(EGLContext eglContext, EGLDisplay eglDisplay) {
221     int[] pbufferAttributes =
222         new int[] {
223           EGL14.EGL_WIDTH, /* width= */ 1,
224           EGL14.EGL_HEIGHT, /* height= */ 1,
225           EGL14.EGL_NONE
226         };
227     EGLSurface eglSurface =
228         Api17.createEglPbufferSurface(
229             eglDisplay, EGL_CONFIG_ATTRIBUTES_RGBA_8888, pbufferAttributes);
230     focusEglSurface(eglDisplay, eglContext, eglSurface, /* width= */ 1, /* height= */ 1);
231   }
232 
233   /**
234    * Creates and focuses a new {@link EGLSurface} wrapping a 1x1 pixel buffer, for HDR rendering
235    * with Rec. 2020 color primaries and using the PQ transfer function.
236    *
237    * @param eglContext The {@link EGLContext} to make current.
238    * @param eglDisplay The {@link EGLDisplay} to attach the surface to.
239    */
240   @RequiresApi(17)
focusPlaceholderEglSurfaceBt2020Pq( EGLContext eglContext, EGLDisplay eglDisplay)241   public static void focusPlaceholderEglSurfaceBt2020Pq(
242       EGLContext eglContext, EGLDisplay eglDisplay) {
243     int[] pbufferAttributes =
244         new int[] {
245           EGL14.EGL_WIDTH,
246           /* width= */ 1,
247           EGL14.EGL_HEIGHT,
248           /* height= */ 1,
249           EGL_GL_COLORSPACE_KHR,
250           EGL_GL_COLORSPACE_BT2020_PQ_EXT,
251           EGL14.EGL_NONE
252         };
253     EGLSurface eglSurface =
254         Api17.createEglPbufferSurface(
255             eglDisplay, EGL_CONFIG_ATTRIBUTES_RGBA_1010102, pbufferAttributes);
256     focusEglSurface(eglDisplay, eglContext, eglSurface, /* width= */ 1, /* height= */ 1);
257   }
258 
259   /**
260    * If there is an OpenGl error, logs the error and if {@link #glAssertionsEnabled} is true throws
261    * a {@link GlException}.
262    */
checkGlError()263   public static void checkGlError() {
264     int lastError = GLES20.GL_NO_ERROR;
265     int error;
266     while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
267       Log.e(TAG, "glError: " + gluErrorString(error));
268       lastError = error;
269     }
270     if (lastError != GLES20.GL_NO_ERROR) {
271       throwGlException("glError: " + gluErrorString(lastError));
272     }
273   }
274 
275   /**
276    * Asserts the texture size is valid.
277    *
278    * @param width The width for a texture.
279    * @param height The height for a texture.
280    * @throws GlException If the texture width or height is invalid.
281    */
assertValidTextureSize(int width, int height)282   public static void assertValidTextureSize(int width, int height) {
283     // TODO(b/201293185): Consider handling adjustments for sizes > GL_MAX_TEXTURE_SIZE
284     //  (ex. downscaling appropriately) in a FrameProcessor instead of asserting incorrect values.
285 
286     // For valid GL sizes, see:
287     // https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glTexImage2D.xml
288     int[] maxTextureSizeBuffer = new int[1];
289     GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSizeBuffer, 0);
290     int maxTextureSize = maxTextureSizeBuffer[0];
291     if (width < 0 || height < 0) {
292       throwGlException("width or height is less than 0");
293     }
294     if (width > maxTextureSize || height > maxTextureSize) {
295       throwGlException("width or height is greater than GL_MAX_TEXTURE_SIZE " + maxTextureSize);
296     }
297   }
298 
299   /**
300    * Makes the specified {@code eglSurface} the render target, using a viewport of {@code width} by
301    * {@code height} pixels.
302    */
303   @RequiresApi(17)
focusEglSurface( EGLDisplay eglDisplay, EGLContext eglContext, EGLSurface eglSurface, int width, int height)304   public static void focusEglSurface(
305       EGLDisplay eglDisplay, EGLContext eglContext, EGLSurface eglSurface, int width, int height) {
306     Api17.focusRenderTarget(
307         eglDisplay, eglContext, eglSurface, /* framebuffer= */ 0, width, height);
308   }
309 
310   /**
311    * Makes the specified {@code framebuffer} the render target, using a viewport of {@code width} by
312    * {@code height} pixels.
313    */
314   @RequiresApi(17)
focusFramebuffer( EGLDisplay eglDisplay, EGLContext eglContext, EGLSurface eglSurface, int framebuffer, int width, int height)315   public static void focusFramebuffer(
316       EGLDisplay eglDisplay,
317       EGLContext eglContext,
318       EGLSurface eglSurface,
319       int framebuffer,
320       int width,
321       int height) {
322     Api17.focusRenderTarget(eglDisplay, eglContext, eglSurface, framebuffer, width, height);
323   }
324 
325   /**
326    * Deletes a GL texture.
327    *
328    * @param textureId The ID of the texture to delete.
329    */
deleteTexture(int textureId)330   public static void deleteTexture(int textureId) {
331     GLES20.glDeleteTextures(/* n= */ 1, new int[] {textureId}, /* offset= */ 0);
332     checkGlError();
333   }
334 
335   /**
336    * Destroys the {@link EGLContext} identified by the provided {@link EGLDisplay} and {@link
337    * EGLContext}.
338    */
339   @RequiresApi(17)
destroyEglContext( @ullable EGLDisplay eglDisplay, @Nullable EGLContext eglContext)340   public static void destroyEglContext(
341       @Nullable EGLDisplay eglDisplay, @Nullable EGLContext eglContext) {
342     Api17.destroyEglContext(eglDisplay, eglContext);
343   }
344 
345   /**
346    * Allocates a FloatBuffer with the given data.
347    *
348    * @param data Used to initialize the new buffer.
349    */
createBuffer(float[] data)350   public static FloatBuffer createBuffer(float[] data) {
351     return (FloatBuffer) createBuffer(data.length).put(data).flip();
352   }
353 
354   /**
355    * Allocates a FloatBuffer.
356    *
357    * @param capacity The new buffer's capacity, in floats.
358    */
createBuffer(int capacity)359   public static FloatBuffer createBuffer(int capacity) {
360     ByteBuffer byteBuffer = ByteBuffer.allocateDirect(capacity * C.BYTES_PER_FLOAT);
361     return byteBuffer.order(ByteOrder.nativeOrder()).asFloatBuffer();
362   }
363 
364   /**
365    * Loads a file from the assets folder.
366    *
367    * @param context The {@link Context}.
368    * @param assetPath The path to the file to load, from the assets folder.
369    * @return The content of the file to load.
370    * @throws IOException If the file couldn't be read.
371    */
loadAsset(Context context, String assetPath)372   public static String loadAsset(Context context, String assetPath) throws IOException {
373     @Nullable InputStream inputStream = null;
374     try {
375       inputStream = context.getAssets().open(assetPath);
376       return Util.fromUtf8Bytes(Util.toByteArray(inputStream));
377     } finally {
378       Util.closeQuietly(inputStream);
379     }
380   }
381 
382   /**
383    * Creates a GL_TEXTURE_EXTERNAL_OES with default configuration of GL_LINEAR filtering and
384    * GL_CLAMP_TO_EDGE wrapping.
385    */
createExternalTexture()386   public static int createExternalTexture() {
387     int texId = generateTexture();
388     bindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId);
389     return texId;
390   }
391 
392   /**
393    * Returns the texture identifier for a newly-allocated texture with the specified dimensions.
394    *
395    * @param width of the new texture in pixels
396    * @param height of the new texture in pixels
397    */
createTexture(int width, int height)398   public static int createTexture(int width, int height) {
399     assertValidTextureSize(width, height);
400     int texId = generateTexture();
401     bindTexture(GLES20.GL_TEXTURE_2D, texId);
402     ByteBuffer byteBuffer = ByteBuffer.allocateDirect(width * height * 4);
403     GLES20.glTexImage2D(
404         GLES20.GL_TEXTURE_2D,
405         /* level= */ 0,
406         GLES20.GL_RGBA,
407         width,
408         height,
409         /* border= */ 0,
410         GLES20.GL_RGBA,
411         GLES20.GL_UNSIGNED_BYTE,
412         byteBuffer);
413     checkGlError();
414     return texId;
415   }
416 
417   /** Returns a new GL texture identifier. */
generateTexture()418   private static int generateTexture() {
419     checkEglException(
420         !Util.areEqual(EGL14.eglGetCurrentContext(), EGL14.EGL_NO_CONTEXT), "No current context");
421 
422     int[] texId = new int[1];
423     GLES20.glGenTextures(/* n= */ 1, texId, /* offset= */ 0);
424     checkGlError();
425     return texId[0];
426   }
427 
428   /**
429    * Binds the texture of the given type with default configuration of GL_LINEAR filtering and
430    * GL_CLAMP_TO_EDGE wrapping.
431    *
432    * @param texId The texture identifier.
433    * @param textureTarget The target to which the texture is bound, e.g. {@link
434    *     GLES20#GL_TEXTURE_2D} for a two-dimensional texture or {@link
435    *     GLES11Ext#GL_TEXTURE_EXTERNAL_OES} for an external texture.
436    */
bindTexture(int textureTarget, int texId)437   /* package */ static void bindTexture(int textureTarget, int texId) {
438     GLES20.glBindTexture(textureTarget, texId);
439     checkGlError();
440     GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
441     checkGlError();
442     GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
443     checkGlError();
444     GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
445     checkGlError();
446     GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
447     checkGlError();
448   }
449 
450   /**
451    * Returns a new framebuffer for the texture.
452    *
453    * @param texId The identifier of the texture to attach to the framebuffer.
454    */
createFboForTexture(int texId)455   public static int createFboForTexture(int texId) {
456     checkEglException(
457         !Util.areEqual(EGL14.eglGetCurrentContext(), EGL14.EGL_NO_CONTEXT), "No current context");
458 
459     int[] fboId = new int[1];
460     GLES20.glGenFramebuffers(/* n= */ 1, fboId, /* offset= */ 0);
461     checkGlError();
462     GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId[0]);
463     checkGlError();
464     GLES20.glFramebufferTexture2D(
465         GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, texId, 0);
466     checkGlError();
467     return fboId[0];
468   }
469 
throwGlException(String errorMsg)470   /* package */ static void throwGlException(String errorMsg) {
471     if (glAssertionsEnabled) {
472       throw new GlException(errorMsg);
473     } else {
474       Log.e(TAG, errorMsg);
475     }
476   }
477 
checkEglException(boolean expression, String errorMessage)478   private static void checkEglException(boolean expression, String errorMessage) {
479     if (!expression) {
480       throwGlException(errorMessage);
481     }
482   }
483 
checkEglException(String errorMessage)484   private static void checkEglException(String errorMessage) {
485     int error = EGL14.eglGetError();
486     checkEglException(error == EGL14.EGL_SUCCESS, errorMessage + ", error code: " + error);
487   }
488 
489   @RequiresApi(17)
490   private static final class Api17 {
Api17()491     private Api17() {}
492 
493     @DoNotInline
createEglDisplay()494     public static EGLDisplay createEglDisplay() {
495       EGLDisplay eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
496       checkEglException(!eglDisplay.equals(EGL14.EGL_NO_DISPLAY), "No EGL display.");
497       if (!EGL14.eglInitialize(
498           eglDisplay,
499           /* unusedMajor */ new int[1],
500           /* majorOffset= */ 0,
501           /* unusedMinor */ new int[1],
502           /* minorOffset= */ 0)) {
503         throwGlException("Error in eglInitialize.");
504       }
505       checkGlError();
506       return eglDisplay;
507     }
508 
509     @DoNotInline
createEglContext( EGLDisplay eglDisplay, int version, int[] configAttributes)510     public static EGLContext createEglContext(
511         EGLDisplay eglDisplay, int version, int[] configAttributes) {
512       int[] contextAttributes = {EGL14.EGL_CONTEXT_CLIENT_VERSION, version, EGL14.EGL_NONE};
513       EGLContext eglContext =
514           EGL14.eglCreateContext(
515               eglDisplay,
516               getEglConfig(eglDisplay, configAttributes),
517               EGL14.EGL_NO_CONTEXT,
518               contextAttributes,
519               /* offset= */ 0);
520       if (eglContext == null) {
521         EGL14.eglTerminate(eglDisplay);
522         throwGlException(
523             "eglCreateContext() failed to create a valid context. The device may not support EGL"
524                 + " version "
525                 + version);
526       }
527       checkGlError();
528       return eglContext;
529     }
530 
531     @DoNotInline
getEglSurface( EGLDisplay eglDisplay, Object surface, int[] configAttributes, int[] windowSurfaceAttributes)532     public static EGLSurface getEglSurface(
533         EGLDisplay eglDisplay,
534         Object surface,
535         int[] configAttributes,
536         int[] windowSurfaceAttributes) {
537       EGLSurface eglSurface =
538           EGL14.eglCreateWindowSurface(
539               eglDisplay,
540               getEglConfig(eglDisplay, configAttributes),
541               surface,
542               windowSurfaceAttributes,
543               /* offset= */ 0);
544       checkEglException("Error creating surface");
545       return eglSurface;
546     }
547 
548     @DoNotInline
createEglPbufferSurface( EGLDisplay eglDisplay, int[] configAttributes, int[] pbufferAttributes)549     public static EGLSurface createEglPbufferSurface(
550         EGLDisplay eglDisplay, int[] configAttributes, int[] pbufferAttributes) {
551       EGLSurface eglSurface =
552           EGL14.eglCreatePbufferSurface(
553               eglDisplay,
554               getEglConfig(eglDisplay, configAttributes),
555               pbufferAttributes,
556               /* offset= */ 0);
557       checkEglException("Error creating surface");
558       return eglSurface;
559     }
560 
561     @DoNotInline
focusRenderTarget( EGLDisplay eglDisplay, EGLContext eglContext, EGLSurface eglSurface, int framebuffer, int width, int height)562     public static void focusRenderTarget(
563         EGLDisplay eglDisplay,
564         EGLContext eglContext,
565         EGLSurface eglSurface,
566         int framebuffer,
567         int width,
568         int height) {
569       int[] boundFramebuffer = new int[1];
570       GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, boundFramebuffer, /* offset= */ 0);
571       if (boundFramebuffer[0] != framebuffer) {
572         GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebuffer);
573       }
574       checkGlError();
575       EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
576       checkEglException("Error making context current");
577       GLES20.glViewport(/* x= */ 0, /* y= */ 0, width, height);
578       checkGlError();
579     }
580 
581     @DoNotInline
destroyEglContext( @ullable EGLDisplay eglDisplay, @Nullable EGLContext eglContext)582     public static void destroyEglContext(
583         @Nullable EGLDisplay eglDisplay, @Nullable EGLContext eglContext) {
584       if (eglDisplay == null) {
585         return;
586       }
587       EGL14.eglMakeCurrent(
588           eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
589       checkEglException("Error releasing context");
590       if (eglContext != null) {
591         EGL14.eglDestroyContext(eglDisplay, eglContext);
592         checkEglException("Error destroying context");
593       }
594       EGL14.eglReleaseThread();
595       checkEglException("Error releasing thread");
596       EGL14.eglTerminate(eglDisplay);
597       checkEglException("Error terminating display");
598     }
599 
600     @DoNotInline
getEglConfig(EGLDisplay eglDisplay, int[] attributes)601     private static EGLConfig getEglConfig(EGLDisplay eglDisplay, int[] attributes) {
602       EGLConfig[] eglConfigs = new EGLConfig[1];
603       if (!EGL14.eglChooseConfig(
604           eglDisplay,
605           attributes,
606           /* attrib_listOffset= */ 0,
607           eglConfigs,
608           /* configsOffset= */ 0,
609           /* config_size= */ 1,
610           /* unusedNumConfig */ new int[1],
611           /* num_configOffset= */ 0)) {
612         throwGlException("eglChooseConfig failed.");
613       }
614       return eglConfigs[0];
615     }
616   }
617 }
618