FrameBuffer.kt
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.graphics.opengl
import android.hardware.HardwareBuffer
import android.opengl.GLES20
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.graphics.opengl.egl.EGLSpec
import androidx.opengl.EGLExt
import androidx.opengl.EGLImageKHR
/**
* Object that enables rendering into a [HardwareBuffer] by
* creating a frame buffer object from it by leveraging Android
* specific EGL extensions to create an [EGLImageKHR] object
* that is loaded as a texture.
*
* @param egl [EGLSpec] used to specify EGL version and call various EGL methods
* @param hardwareBuffer the [HardwareBuffer] that this class wraps and used to generate a
* [EGLImageKHR] object
*/
@RequiresApi(Build.VERSION_CODES.O)
class FrameBuffer(
private val egl: EGLSpec,
val hardwareBuffer: HardwareBuffer,
) : AutoCloseable {
private var eglImage: EGLImageKHR?
private var texture: Int = -1
/**
* Return the corresponding FrameBuffer identifier.
*/
internal var frameBuffer: Int = -1
private set
/**
* Boolean that tells if the frame buffer is currently closed
*/
var isClosed = false
private set
// Int array used for creation of fbos/textures
private val buffer = IntArray(1)
init {
val image: EGLImageKHR = egl.eglCreateImageFromHardwareBuffer(hardwareBuffer)
?: throw IllegalArgumentException("Unable to create EGLImage from HardwareBuffer")
eglImage = image
GLES20.glGenTextures(1, buffer, 0)
texture = buffer[0]
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture)
EGLExt.glEGLImageTargetTexture2DOES(GLES20.GL_TEXTURE_2D, image)
GLES20.glGenFramebuffers(1, buffer, 0)
frameBuffer = buffer[0]
}
/**
* Binds this frame buffer to the read and draw framebuffer targets if it's not closed.
* If the frame buffer is already closed this method will do nothing.
*/
fun makeCurrent() {
if (!isClosed) {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffer)
GLES20.glFramebufferTexture2D(
GLES20.GL_FRAMEBUFFER,
GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D,
texture,
0
)
}
}
/**
* Closes out the frame buffer, freeing all resources within it. This should be done only
* when the frame buffer is no longer needed or being accessed.
*/
override fun close() {
buffer[0] = frameBuffer
GLES20.glDeleteBuffers(1, buffer, 0)
frameBuffer = -1
buffer[0] = texture
GLES20.glDeleteTextures(1, buffer, 0)
texture = -1
eglImage?.let { egl.eglDestroyImageKHR(it) }
eglImage = null
hardwareBuffer.close()
isClosed = true
}
}