1 /*
2  * Copyright 2022 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
18 
19 import android.hardware.HardwareBuffer
20 import android.opengl.GLES20
21 import android.os.Build
22 import androidx.annotation.RequiresApi
23 import androidx.graphics.opengl.egl.EGLSpec
24 import androidx.opengl.EGLExt
25 import androidx.opengl.EGLImageKHR
26 
27 /**
28  * Object that enables rendering into a [HardwareBuffer] by creating a frame buffer object from it
29  * by leveraging Android specific EGL extensions to create an [EGLImageKHR] object that is loaded as
30  * a texture.
31  *
32  * @param egl [EGLSpec] used to specify EGL version and call various EGL methods
33  * @param hardwareBuffer the [HardwareBuffer] that this class wraps and used to generate a
34  *   [EGLImageKHR] object
35  */
36 @RequiresApi(Build.VERSION_CODES.O)
37 class FrameBuffer(
38     private val egl: EGLSpec,
39     val hardwareBuffer: HardwareBuffer,
40 ) : AutoCloseable {
41 
42     private var eglImage: EGLImageKHR?
43     private var texture: Int = -1
44 
45     /** Return the corresponding FrameBuffer identifier. */
46     internal var frameBuffer: Int = -1
47         private set
48 
49     /** Boolean that tells if the frame buffer is currently closed */
50     var isClosed = false
51         private set
52 
53     // Int array used for creation of fbos/textures
54     private val tmpBuffer = IntArray(1)
55 
56     init {
57         val image: EGLImageKHR =
58             egl.eglCreateImageFromHardwareBuffer(hardwareBuffer)
59                 ?: throw IllegalArgumentException("Unable to create EGLImage from HardwareBuffer")
60         eglImage = image
61 
62         GLES20.glGenTextures(1, tmpBuffer, 0)
63         texture = tmpBuffer[0]
64 
65         GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture)
66         EGLExt.glEGLImageTargetTexture2DOES(GLES20.GL_TEXTURE_2D, image)
67 
68         GLES20.glGenFramebuffers(1, tmpBuffer, 0)
69         frameBuffer = tmpBuffer[0]
70     }
71 
72     /**
73      * Binds this frame buffer to the read and draw framebuffer targets if it's not closed. If the
74      * frame buffer is already closed this method will do nothing.
75      */
makeCurrentnull76     fun makeCurrent() {
77         if (!isClosed) {
78             GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffer)
79             GLES20.glFramebufferTexture2D(
80                 GLES20.GL_FRAMEBUFFER,
81                 GLES20.GL_COLOR_ATTACHMENT0,
82                 GLES20.GL_TEXTURE_2D,
83                 texture,
84                 0
85             )
86         }
87     }
88 
89     /**
90      * Closes out the frame buffer, freeing all resources within it. This should be done only when
91      * the frame buffer is no longer needed or being accessed.
92      */
closenull93     override fun close() {
94         if (!isClosed) {
95             tmpBuffer[0] = frameBuffer
96             GLES20.glDeleteFramebuffers(1, tmpBuffer, 0)
97             frameBuffer = -1
98 
99             tmpBuffer[0] = texture
100             GLES20.glDeleteTextures(1, tmpBuffer, 0)
101             texture = -1
102 
103             eglImage?.let { egl.eglDestroyImageKHR(it) }
104             eglImage = null
105             hardwareBuffer.close()
106             isClosed = true
107         }
108     }
109 }
110