1 /* <lambda>null2 * Copyright 2021 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 17 package androidx.graphics.opengl.egl 18 19 import android.opengl.EGL14 20 import android.opengl.EGLConfig 21 import android.opengl.EGLContext 22 import android.opengl.EGLSurface 23 import android.opengl.GLES20 24 import androidx.opengl.EGLExt 25 import androidx.opengl.EGLExt.Companion.EGL_KHR_SURFACELESS_CONTEXT 26 27 /** 28 * Class responsible for configuration of EGL related resources. This includes initialization of the 29 * corresponding EGL Display as well as EGL Context, among other EGL related facilities. 30 */ 31 class EGLManager(eglSpec: EGLSpec = EGLSpec.V14) { 32 33 private var mEglConfig: EGLConfig? = null 34 35 /** Offscreen pixel buffer surface */ 36 private var mPBufferSurface: EGLSurface = EGL14.EGL_NO_SURFACE 37 private var mEglContext: EGLContext = EGL14.EGL_NO_CONTEXT 38 private var mEglVersion = EGLVersion.Unknown 39 private val mEglSpec = eglSpec 40 private var mEglExtensions: Set<String>? = null 41 private var mIsSingleBuffered: Boolean = false 42 private var mQueryResult: IntArray? = null 43 44 /** 45 * Initialize the EGLManager. This initializes the default display as well as queries the 46 * supported extensions 47 */ 48 fun initialize() { 49 mEglContext.let { 50 if (it === EGL14.EGL_NO_CONTEXT) { 51 mEglVersion = eglSpec.eglInitialize() 52 mEglExtensions = 53 EGLExt.parseExtensions(eglSpec.eglQueryString(EGL14.EGL_EXTENSIONS)) 54 } 55 } 56 } 57 58 /** 59 * Attempt to load an [EGLConfig] instance from the given [EGLConfigAttributes]. If the 60 * [EGLConfig] could not be loaded this returns null 61 */ 62 fun loadConfig(configAttributes: EGLConfigAttributes): EGLConfig? = 63 eglSpec.loadConfig(configAttributes) 64 65 /** 66 * Creates an [EGLContext] from the given [EGLConfig] returning null if the context could not be 67 * created 68 * 69 * @throws EGLException if the default surface could not be made current after context creation 70 */ 71 fun createContext(config: EGLConfig): EGLContext { 72 val eglContext = eglSpec.eglCreateContext(config) 73 if (eglContext !== EGL14.EGL_NO_CONTEXT) { 74 val pbBufferSurface: EGLSurface = 75 if (isExtensionSupported(EGL_KHR_SURFACELESS_CONTEXT)) { 76 EGL14.EGL_NO_SURFACE 77 } else { 78 val configAttrs = EGLConfigAttributes { 79 EGL14.EGL_WIDTH to 1 80 EGL14.EGL_HEIGHT to 1 81 } 82 eglSpec.eglCreatePBufferSurface(config, configAttrs) 83 } 84 if (!eglSpec.eglMakeCurrent(eglContext, pbBufferSurface, pbBufferSurface)) { 85 throw EGLException(eglSpec.eglGetError(), "Unable to make default surface current") 86 } 87 mPBufferSurface = pbBufferSurface 88 mEglContext = eglContext 89 mEglConfig = config 90 } else { 91 mPBufferSurface = EGL14.EGL_NO_SURFACE 92 mEglContext = EGL14.EGL_NO_CONTEXT 93 mEglConfig = null 94 } 95 return eglContext 96 } 97 98 /** 99 * Release the resources allocated by EGLManager. This will destroy the corresponding EGLContext 100 * instance if it was previously initialized. The configured EGLVersion as well as EGLExtensions 101 */ 102 fun release() { 103 mEglContext.let { 104 if (it != EGL14.EGL_NO_CONTEXT) { 105 eglSpec.eglDestroyContext(it) 106 mPBufferSurface.let { pbBufferSurface -> 107 if (pbBufferSurface != EGL14.EGL_NO_SURFACE) { 108 eglSpec.eglDestroySurface(pbBufferSurface) 109 } 110 } 111 mPBufferSurface = EGL14.EGL_NO_SURFACE 112 eglSpec.eglMakeCurrent( 113 EGL14.EGL_NO_CONTEXT, 114 EGL14.EGL_NO_SURFACE, 115 EGL14.EGL_NO_SURFACE 116 ) 117 mEglVersion = EGLVersion.Unknown 118 mEglContext = EGL14.EGL_NO_CONTEXT 119 mEglConfig = null 120 mEglExtensions = null 121 } 122 } 123 } 124 125 val eglSpec: EGLSpec 126 @JvmName("getEGLSpec") get() = mEglSpec 127 128 /** 129 * Returns the EGL version that is supported. This parameter is configured after [initialize] is 130 * invoked. 131 */ 132 val eglVersion: EGLVersion 133 @JvmName("getEGLVersion") get() = mEglVersion 134 135 /** 136 * Returns the current EGLContext. This parameter is configured after [initialize] is invoked 137 */ 138 val eglContext: EGLContext? 139 @JvmName("getEGLContext") get() = mEglContext 140 141 /** 142 * Returns the [EGLConfig] used to load the current [EGLContext]. This is configured after 143 * [createContext] is invoked. 144 */ 145 val eglConfig: EGLConfig? 146 @JvmName("getEGLConfig") get() = mEglConfig 147 148 /** 149 * Determines whether the extension with the provided name is supported. The string provided is 150 * expected to be one of the named extensions defined within the OpenGL extension documentation. 151 * 152 * See [EGLExt] for additional documentation for given extension name constants and 153 * descriptions. 154 * 155 * The set of supported extensions is configured after [initialize] is invoked. Attempts to 156 * query support for any extension beforehand will return false. 157 */ 158 fun isExtensionSupported(extensionName: String): Boolean = 159 mEglExtensions?.contains(extensionName) ?: false 160 161 /** 162 * Binds the current context to the given draw and read surfaces. The draw surface is used for 163 * all operations except for any pixel data read back or copy operations which are taken from 164 * the read surface. 165 * 166 * The same EGLSurface may be specified for both draw and read surfaces. 167 * 168 * If the context is not previously configured, the only valid parameters for the draw and read 169 * surfaces is [EGL14.EGL_NO_SURFACE]. This is useful to make sure there is always a surface 170 * specified and to release the current context without assigning a new one. 171 * 172 * See https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglMakeCurrent.xhtml 173 * 174 * @param drawSurface Surface used for all operations that involve writing pixel information 175 * @param readSurface Surface used for pixel data read back or copy operations. By default this 176 * is the same as [drawSurface] 177 */ 178 @JvmOverloads 179 fun makeCurrent(drawSurface: EGLSurface, readSurface: EGLSurface = drawSurface): Boolean { 180 val result = eglSpec.eglMakeCurrent(mEglContext, drawSurface, readSurface) 181 if (result) { 182 querySurface(drawSurface) 183 } 184 return result 185 } 186 187 /** 188 * Post EGL surface color buffer to a native window. If the current drawing surface is single 189 * buffered this will flush the buffer 190 */ 191 fun swapAndFlushBuffers() { 192 if (mIsSingleBuffered) { 193 GLES20.glFlush() 194 } 195 eglSpec.eglSwapBuffers(currentDrawSurface) 196 } 197 198 /** 199 * Returns the default surface. This can be an offscreen pixel buffer surface or 200 * [EGL14.EGL_NO_SURFACE] if the surfaceless context extension is supported. 201 */ 202 val defaultSurface: EGLSurface 203 get() = mPBufferSurface 204 205 /** Returns the current surface used for drawing pixel content */ 206 val currentDrawSurface: EGLSurface 207 get() = eglSpec.eglGetCurrentDrawSurface() 208 209 /** Returns the current surface used for reading back or copying pixels */ 210 val currentReadSurface: EGLSurface 211 get() = eglSpec.eglGetCurrentReadSurface() 212 213 /** Helper method to query properties of the given surface */ 214 private fun querySurface(surface: EGLSurface) { 215 if (surface == EGL14.EGL_NO_SURFACE) { 216 mIsSingleBuffered = false 217 } else { 218 val resultArray = mQueryResult ?: IntArray(1).also { mQueryResult = it } 219 if (eglSpec.eglQuerySurface(surface, EGL14.EGL_RENDER_BUFFER, resultArray, 0)) { 220 mIsSingleBuffered = resultArray[0] == EGL14.EGL_SINGLE_BUFFER 221 } 222 } 223 } 224 225 companion object { 226 private const val TAG = "EglManager" 227 } 228 } 229