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