1 /*
<lambda>null2  * Copyright 2023 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.opengl.Matrix
22 import android.os.Build
23 import android.util.Log
24 import android.view.SurfaceHolder
25 import android.view.SurfaceView
26 import androidx.annotation.IntRange
27 import androidx.annotation.RequiresApi
28 import androidx.annotation.WorkerThread
29 import androidx.graphics.lowlatency.BufferInfo
30 import androidx.graphics.lowlatency.BufferTransformHintResolver
31 import androidx.graphics.lowlatency.BufferTransformer
32 import androidx.graphics.opengl.egl.EGLManager
33 import androidx.graphics.opengl.egl.EGLSpec
34 import androidx.graphics.surface.SurfaceControlCompat
35 import androidx.hardware.DefaultFlags
36 import androidx.hardware.DefaultNumBuffers
37 import androidx.hardware.HardwareBufferFormat
38 import androidx.hardware.HardwareBufferUsage
39 import androidx.hardware.SyncFenceCompat
40 import java.lang.IllegalArgumentException
41 import java.lang.IllegalStateException
42 import java.util.concurrent.CountDownLatch
43 
44 /**
45  * Class responsible for supporting rendering to frame buffer objects that are backed by
46  * [HardwareBuffer] instances. This provides for more flexibility in OpenGL rendering as it supports
47  * configuration of the number of buffers within the underlying swap chain, pixel format of the
48  * buffers as well as fine grained control over synchronization of buffer content.
49  */
50 @RequiresApi(Build.VERSION_CODES.Q)
51 class GLFrameBufferRenderer
52 internal constructor(
53     private val surfaceControlProvider: SurfaceControlProvider,
54     callback: Callback,
55     private val mFormat: Int,
56     private val mUsage: Long,
57     private val mMaxBuffers: Int,
58     private val mSyncStrategy: SyncStrategy,
59     glRenderer: GLRenderer?
60 ) {
61 
62     /** Builder used to create a [GLFrameBufferRenderer] with various configurations */
63     class Builder {
64         private var mBufferFormat = HardwareBuffer.RGBA_8888
65         private var mUsageFlags = DefaultFlags
66         private var mMaxBuffers = DefaultNumBuffers
67         private var mGLRenderer: GLRenderer? = null
68         private var mSyncStrategy: SyncStrategy = SyncStrategy.ALWAYS
69         private val mSurfaceControlProvider: SurfaceControlProvider
70         private val mCallback: Callback
71 
72         /**
73          * Create a new [GLFrameBufferRenderer.Builder] with the provided [SurfaceView] to be the
74          * parent of the [SurfaceControlCompat] that is presented on screen.
75          *
76          * @param surfaceView SurfaceView to be the parent of the [SurfaceControlCompat] instance
77          *   used for presenting rendered content on screen
78          * @param callback Callback used to render content within the corresponding buffers as well
79          *   as optionally configuring [SurfaceControlCompat.Transaction] to present contents to the
80          *   display
81          */
82         constructor(surfaceView: SurfaceView, callback: Callback) {
83             mSurfaceControlProvider = SurfaceViewProvider(surfaceView)
84             mCallback = callback
85         }
86 
87         /**
88          * Creates a new [GLFrameBufferRenderer.Builder] with the provided [SurfaceControlCompat] as
89          * the parent [SurfaceControlCompat] for presenting contents to the display.
90          *
91          * It is the responsibility of the caller to release the provided [SurfaceControlCompat]
92          * instance as the [GLFrameBufferRenderer] will consume but not release it.
93          *
94          * @param parentSurfaceControl Parent [SurfaceControlCompat] instance.
95          * @param width Logical width of the content to render. This dimension matches what is
96          *   provided from [SurfaceHolder.Callback.surfaceChanged].
97          * @param height Logical height of the content to render. This dimension matches what is
98          *   provided from [SurfaceHolder.Callback.surfaceChanged].
99          * @param transformHint Hint used to specify how to pre-rotate content to optimize
100          *   consumption of content by the display without having to introduce an additional GPU
101          *   pass to handle rotation.
102          */
103         internal constructor(
104             parentSurfaceControl: SurfaceControlCompat,
105             width: Int,
106             height: Int,
107             transformHint: Int,
108             callback: Callback
109         ) {
110             mSurfaceControlProvider =
111                 DefaultSurfaceControlProvider(parentSurfaceControl, width, height, transformHint)
112             mCallback = callback
113         }
114 
115         /**
116          * Specify the [SyncStrategy] used for determining when to create [SyncFenceCompat] objects
117          * in order to handle synchronization. The [SyncFenceCompat] instance created according to
118          * the algorithm specified in the provided [SyncStrategy] will be passed to the
119          * corresponding [SurfaceControlCompat.Transaction.setBuffer] call in order to ensure the
120          * underlying buffer is not presented by the display until the fence signals.
121          *
122          * @param syncStrategy [SyncStrategy] used to determine when to create synchronization
123          *   boundaries for buffer consumption. The default is [SyncStrategy.ALWAYS], indicating a
124          *   fence should always be created after a request to render has been made.
125          * @return The builder instance
126          */
127         fun setSyncStrategy(syncStrategy: SyncStrategy): Builder {
128             mSyncStrategy = syncStrategy
129             return this
130         }
131 
132         /**
133          * Specify the buffer format of the underlying buffers being rendered into by the created
134          * [GLFrameBufferRenderer]. The set of valid formats is implementation-specific and may
135          * depend on additional EGL extensions. The particular valid combinations for a given
136          * Android version and implementation should be documented by that version.
137          *
138          * [HardwareBuffer.RGBA_8888] and [HardwareBuffer.RGBX_8888] are guaranteed to be supported.
139          * However, consumers are recommended to query the desired HardwareBuffer configuration
140          * using [HardwareBuffer.isSupported].
141          *
142          * See: khronos.org/registry/EGL/extensions/ANDROID/EGL_ANDROID_get_native_client_buffer.txt
143          *
144          * @param format Pixel format of the buffers to be rendered into. The default is RGBA_8888.
145          * @return The builder instance
146          */
147         fun setBufferFormat(@HardwareBufferFormat format: Int): Builder {
148             mBufferFormat = format
149             return this
150         }
151 
152         /**
153          * Specify the maximum number of buffers used within the swap chain of the
154          * [GLFrameBufferRenderer]. If 1 is specified, then the created [GLFrameBufferRenderer] is
155          * running in "single buffer mode". In this case consumption of the buffer content would
156          * need to be coordinated with the [SyncFenceCompat] instance specified by the corresponding
157          * [SyncStrategy] algorithm
158          *
159          * @param numBuffers The number of buffers within the swap chain to be consumed by the
160          *   created [GLFrameBufferRenderer]. This must be greater than zero. The default number of
161          *   buffers used is 3.
162          * @return The builder instance
163          * @see [setSyncStrategy].
164          */
165         fun setMaxBuffers(@IntRange(from = 1, to = 64) numBuffers: Int): Builder {
166             require(numBuffers > 0) { "Must have at least 1 buffer" }
167             mMaxBuffers = numBuffers
168             return this
169         }
170 
171         /**
172          * Specify the usage flags to be configured on the underlying [HardwareBuffer] instances
173          * created by the [GLFrameBufferRenderer].
174          *
175          * @param usageFlags Usage flags to be configured on the created [HardwareBuffer] instances
176          *   that the [GLFrameBufferRenderer] will render into. Must be one of
177          *   [HardwareBufferUsage]. Note that the provided flags here are combined with the
178          *   following mandatory default flags, [HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE],
179          *   [HardwareBuffer.USAGE_GPU_COLOR_OUTPUT] and [HardwareBuffer.USAGE_COMPOSER_OVERLAY]
180          * @return The builder instance
181          */
182         fun setUsageFlags(@HardwareBufferUsage usageFlags: Long): Builder {
183             mUsageFlags = usageFlags or DefaultFlags
184             return this
185         }
186 
187         /**
188          * Configure the [GLRenderer] instance to be used by the [GLFrameBufferRenderer]. By default
189          * this parameter is null indicating that the [GLFrameBufferRenderer] will create and manage
190          * its own [GLRenderer]. This is useful to share the same OpenGL resources and thread across
191          * multiple [GLFrameBufferRenderer] instances.
192          *
193          * @param glRenderer The [GLRenderer] used for leveraging OpenGL resources including the GL
194          *   thread
195          * @return The builder instance
196          */
197         fun setGLRenderer(glRenderer: GLRenderer?): Builder {
198             mGLRenderer = glRenderer
199             return this
200         }
201 
202         /**
203          * Create the [GLFrameBufferRenderer] with the specified parameters on this [Builder]
204          * instance
205          *
206          * @return The newly created [GLFrameBufferRenderer]
207          */
208         fun build(): GLFrameBufferRenderer {
209             return GLFrameBufferRenderer(
210                 mSurfaceControlProvider,
211                 mCallback,
212                 mBufferFormat,
213                 mUsageFlags,
214                 mMaxBuffers,
215                 mSyncStrategy,
216                 mGLRenderer
217             )
218         }
219     }
220 
221     private var mSurfaceControl: SurfaceControlCompat? = null
222     private var mBufferPool: FrameBufferPool? = null
223     private var mRenderTarget: GLRenderer.RenderTarget? = null
224 
225     private val mIsManagingGLRenderer: Boolean
226     private var mIsReleased = false
227     private val mContextCallbacks =
228         object : GLRenderer.EGLContextCallback {
229             override fun onEGLContextCreated(eglManager: EGLManager) {
230                 // NO-OP
231             }
232 
233             override fun onEGLContextDestroyed(eglManager: EGLManager) {
234                 mBufferPool?.close()
235             }
236         }
237 
238     private val mGLRenderer: GLRenderer
239 
240     init {
241         if (mMaxBuffers < 1) {
242             throw IllegalArgumentException("FrameBufferRenderer must have at least 1 buffer")
243         }
244         val renderer =
245             if (glRenderer == null) {
246                 mIsManagingGLRenderer = true
247                 GLRenderer().apply { start() }
248             } else {
249                 mIsManagingGLRenderer = false
250                 if (!glRenderer.isRunning()) {
251                     throw IllegalStateException(
252                         "The provided GLRenderer must be running prior to " +
253                             "creation of GLFrameBufferRenderer, " +
254                             "did you forget to call GLRenderer#start()?"
255                     )
256                 }
257                 glRenderer
258             }
259         renderer.registerEGLContextCallback(mContextCallbacks)
260         mGLRenderer = renderer
261         surfaceControlProvider.createSurfaceControl(
262             object : SurfaceControlProvider.Callback {
263                 override fun onSurfaceControlDestroyed() {
264                     detachTargets(true)
265                 }
266 
267                 override fun onSurfaceControlCreated(
268                     surfaceControl: SurfaceControlCompat,
269                     width: Int,
270                     height: Int,
271                     bufferTransformer: BufferTransformer,
272                     inverseTransform: Int
273                 ) {
274                     val frameBufferPool =
275                         FrameBufferPool(
276                             bufferTransformer.bufferWidth,
277                             bufferTransformer.bufferHeight,
278                             this@GLFrameBufferRenderer.mFormat,
279                             mUsage,
280                             mMaxBuffers
281                         )
282                     val renderCallback =
283                         createFrameBufferRenderer(
284                             surfaceControl,
285                             inverseTransform,
286                             bufferTransformer,
287                             frameBufferPool,
288                             callback
289                         )
290                     mBufferPool = frameBufferPool
291                     mSurfaceControl = surfaceControl
292                     mRenderTarget = renderer.createRenderTarget(width, height, renderCallback)
293                 }
294 
295                 override fun requestRender(renderComplete: Runnable?) {
296                     drawAsync(renderComplete)
297                 }
298             }
299         )
300     }
301 
302     /**
303      * Queue a [Runnable] to be executed on the GL rendering thread. Note it is important this
304      * [Runnable] does not block otherwise it can stall the GL thread.
305      *
306      * @param runnable to be executed
307      */
308     fun execute(runnable: Runnable) {
309         if (isValid()) {
310             mGLRenderer.execute(runnable)
311         } else {
312             Log.w(
313                 TAG,
314                 "Attempt to execute runnable after " + "GLFrameBufferRenderer has been released"
315             )
316         }
317     }
318 
319     /**
320      * Returns the [HardwareBufferFormat] of the buffers that are being rendered into by this
321      * [GLFrameBufferRenderer]
322      */
323     @HardwareBufferFormat
324     val bufferFormat: Int
325         get() = mFormat
326 
327     /**
328      * Returns the current usage flag hints of the buffers that are being rendered into by this
329      * [GLFrameBufferRenderer]
330      */
331     @HardwareBufferUsage
332     val usageFlags: Long
333         get() = mUsage
334 
335     /**
336      * Returns the [GLRenderer] used for issuing requests to render into the underlying buffers with
337      * OpenGL.
338      */
339     val glRenderer: GLRenderer
340         @JvmName("getGLRenderer") get() = mGLRenderer
341 
342     /**
343      * Returns the [SyncStrategy] used for determining when to create [SyncFenceCompat] objects in
344      * order to handle synchronization. The [SyncFenceCompat] instance created according to the
345      * algorithm specified in the provided [SyncStrategy] will be passed to the corresponding
346      * [SurfaceControlCompat.Transaction.setBuffer] call in order to ensure the underlying buffer is
347      * not presented by the display until the fence signals.
348      */
349     val syncStrategy: SyncStrategy
350         get() = mSyncStrategy
351 
352     /**
353      * Returns the number of buffers within the swap chain used for rendering with this
354      * [GLFrameBufferRenderer]
355      */
356     val maxBuffers: Int
357         get() = mMaxBuffers
358 
359     private var mCurrentFrameBuffer: FrameBuffer? = null
360 
361     internal fun createFrameBufferRenderer(
362         surfaceControl: SurfaceControlCompat,
363         inverseTransform: Int,
364         bufferTransformer: BufferTransformer,
365         frameBufferPool: FrameBufferPool,
366         callback: Callback
367     ): FrameBufferRenderer =
368         FrameBufferRenderer(
369             object : FrameBufferRenderer.RenderCallback {
370 
371                 private val width = bufferTransformer.logicalWidth
372                 private val height = bufferTransformer.logicalHeight
373 
374                 private val bufferInfo =
375                     BufferInfo().apply {
376                         this.width = bufferTransformer.bufferWidth
377                         this.height = bufferTransformer.bufferHeight
378                     }
379 
380                 override fun obtainFrameBuffer(egl: EGLSpec): FrameBuffer {
381                     val currentFrameBuffer = mCurrentFrameBuffer
382                     // Single buffer mode if we already allocated 1 buffer just return the previous
383                     // one
384                     return if (mMaxBuffers == 1 && currentFrameBuffer != null) {
385                         currentFrameBuffer
386                     } else {
387                         frameBufferPool.obtain(egl).also {
388                             bufferInfo.frameBufferId = it.frameBuffer
389                             mCurrentFrameBuffer = it
390                         }
391                     }
392                 }
393 
394                 override fun onDraw(eglManager: EGLManager) {
395                     val buffer = mCurrentFrameBuffer
396                     if (buffer != null && !buffer.isClosed) {
397                         callback.onDrawFrame(
398                             eglManager,
399                             width,
400                             height,
401                             bufferInfo,
402                             bufferTransformer.transform
403                         )
404                     }
405                 }
406 
407                 override fun onDrawComplete(
408                     frameBuffer: FrameBuffer,
409                     syncFenceCompat: SyncFenceCompat?
410                 ) {
411                     if (surfaceControl.isValid() && !frameBuffer.isClosed) {
412                         val transaction =
413                             SurfaceControlCompat.Transaction()
414                                 .setVisibility(surfaceControl, true)
415                                 .setBuffer(
416                                     surfaceControl,
417                                     frameBuffer.hardwareBuffer,
418                                     syncFenceCompat
419                                 ) { releaseFence ->
420                                     if (mGLRenderer.isRunning()) {
421                                         mGLRenderer.execute {
422                                             callback.onBufferReleased(frameBuffer, releaseFence)
423                                         }
424                                     }
425                                     if (mMaxBuffers > 1 || frameBufferPool.isClosed) {
426                                         // Release the previous buffer only if we are not in single
427                                         // buffered
428                                         // mode
429                                         frameBufferPool.release(frameBuffer, releaseFence)
430                                     }
431                                 }
432                         if (inverseTransform != BufferTransformHintResolver.UNKNOWN_TRANSFORM) {
433                             transaction.setBufferTransform(surfaceControl, inverseTransform)
434                         }
435                         callback.onDrawComplete(
436                             surfaceControl,
437                             transaction,
438                             frameBuffer,
439                             syncFenceCompat
440                         )
441                         transaction.commit()
442                     }
443                 }
444             },
445             mSyncStrategy
446         )
447 
448     internal fun drawAsync(onComplete: Runnable? = null) {
449         val renderTarget = mRenderTarget
450         val renderer = mGLRenderer
451         if (renderTarget != null && renderer.isRunning()) {
452             // Register a callback in case the GLRenderer is torn down while we are waiting
453             // for rendering to complete. In this case invoke the drawFinished callback
454             // either if the render is complete or if the GLRenderer is torn down, whatever
455             // comes first
456             val eglContextCallback =
457                 object : GLRenderer.EGLContextCallback {
458                     override fun onEGLContextCreated(eglManager: EGLManager) {
459                         // NO-OP
460                     }
461 
462                     override fun onEGLContextDestroyed(eglManager: EGLManager) {
463                         onComplete?.run()
464                         renderer.unregisterEGLContextCallback(this)
465                     }
466                 }
467             renderer.registerEGLContextCallback(eglContextCallback)
468             mRenderTarget?.requestRender {
469                 onComplete?.run()
470                 renderer.unregisterEGLContextCallback(eglContextCallback)
471             }
472         } else {
473             onComplete?.run()
474         }
475     }
476 
477     /**
478      * Release resources associated with the [GLFrameBufferRenderer]. After this method is invoked,
479      * the [GLFrameBufferRenderer] is in an invalid state and can no longer handle rendering
480      * content.
481      *
482      * @param cancelPending If true, pending render requests are cancelled immediately. If false,
483      *   pending render requests are completed before releasing the renderer.
484      * @param onReleaseCallback Optional callback to be invoked on the underlying OpenGL thread when
485      *   releasing resources has been completed
486      */
487     @JvmOverloads
488     fun release(cancelPending: Boolean, onReleaseCallback: (() -> Unit)? = null) {
489         if (!mIsReleased) {
490             detachTargets(cancelPending, onReleaseCallback)
491             surfaceControlProvider.release()
492 
493             mGLRenderer.unregisterEGLContextCallback(mContextCallbacks)
494             if (mIsManagingGLRenderer) {
495                 mGLRenderer.stop(false)
496             }
497 
498             mIsReleased = true
499         } else {
500             Log.w(TAG, "Attempt to release already released GLFrameBufferRenderer")
501         }
502     }
503 
504     internal fun detachTargets(cancelPending: Boolean, onReleaseComplete: (() -> Unit)? = null) {
505         val frameBufferPool = mBufferPool
506         val renderTarget = mRenderTarget
507         val surfaceControl = mSurfaceControl
508         renderTarget?.detach(cancelPending)
509 
510         mGLRenderer.execute {
511             mCurrentFrameBuffer?.let { buffer -> frameBufferPool?.release(buffer) }
512             surfaceControl?.let { sc ->
513                 SurfaceControlCompat.Transaction().reparent(sc, null).apply {
514                     commit()
515                     close()
516                 }
517                 sc.release()
518             }
519             frameBufferPool?.close()
520             onReleaseComplete?.invoke()
521         }
522         mBufferPool = null
523         mSurfaceControl = null
524         mRenderTarget = null
525     }
526 
527     /**
528      * Determines whether or not the [GLFrameBufferRenderer] is in a valid state. That is the
529      * [release] method has not been called. If this returns false, then subsequent calls to
530      * [render], and [release] are ignored
531      *
532      * @return `true` if this [GLFrameBufferRenderer] has been released, `false` otherwise
533      */
534     fun isValid(): Boolean = !mIsReleased
535 
536     /**
537      * Render content to a buffer and present the result to the display.
538      *
539      * If this [GLFrameBufferRenderer] has been released, that is [isValid] returns `false`, this
540      * call is ignored.
541      */
542     fun render() {
543         if (!mIsReleased) {
544             mRenderTarget?.requestRender()
545         } else {
546             Log.w(TAG, "renderer is released, ignoring request")
547         }
548     }
549 
550     /**
551      * [GLFrameBufferRenderer] callbacks that are invoked to render OpenGL content within the
552      * underlying buffers. This includes an optional callback to be used to configure the underlying
553      * [SurfaceControlCompat.Transaction] used to present content to the display
554      */
555     interface Callback {
556 
557         /**
558          * Callback invoked on the thread backed by the [GLRenderer] to render content into a buffer
559          * with the specified parameters.
560          *
561          * @param eglManager [EGLManager] useful in configuring EGL objects to be used when issuing
562          *   OpenGL commands to render into the front buffered layer
563          * @param width Logical width of the content to render. This dimension matches what is
564          *   provided from [SurfaceHolder.Callback.surfaceChanged]
565          * @param height Logical height of the content to render. This dimension matches what is
566          *   provided from [SurfaceHolder.Callback.surfaceChanged]
567          * @param bufferInfo [BufferInfo] about the buffer that is being rendered into. This
568          *   includes the width and height of the buffer which can be different than the
569          *   corresponding dimensions of the [SurfaceView] provided to the [GLFrameBufferRenderer]
570          *   as pre-rotation can occasionally swap width and height parameters in order to avoid GPU
571          *   composition to rotate content. This should be used as input to [GLES20.glViewport].
572          *   Additionally this also contains a frame buffer identifier that can be used to retarget
573          *   rendering operations to the original destination after rendering into intermediate
574          *   scratch buffers.
575          * @param transform Matrix that should be applied to the rendering in this callback. This
576          *   should be consumed as input to any vertex shader implementations. Buffers are
577          *   pre-rotated in advance in order to avoid unnecessary overhead of GPU composition to
578          *   rotate content in the same install orientation of the display. This is a 4 x 4 matrix
579          *   is represented as a flattened array of 16 floating point values. Consumers are expected
580          *   to leverage [Matrix.multiplyMM] with this parameter alongside any additional
581          *   transformations that are to be applied. For example:
582          * ```
583          * val myMatrix = FloatArray(16)
584          * Matrix.orthoM(
585          *      myMatrix, // matrix
586          *      0, // offset starting index into myMatrix
587          *      0f, // left
588          *      bufferInfo.width.toFloat(), // right
589          *      0f, // bottom
590          *      bufferInfo.height.toFloat(), // top
591          *      -1f, // near
592          *      1f, // far
593          * )
594          * val result = FloatArray(16)
595          * Matrix.multiplyMM(result, 0, myMatrix, 0, transform, 0)
596          * ```
597          *
598          * @sample androidx.graphics.core.samples.glFrameBufferSample
599          */
600         @WorkerThread
601         fun onDrawFrame(
602             eglManager: EGLManager,
603             width: Int,
604             height: Int,
605             bufferInfo: BufferInfo,
606             transform: FloatArray
607         )
608 
609         /**
610          * Optional callback invoked the thread backed by the [GLRenderer] when rendering to a
611          * buffer is complete but before the buffer is submitted to the hardware compositor. This
612          * provides consumers a mechanism for synchronizing the transaction with other
613          * [SurfaceControlCompat] objects that maybe rendered within the scene.
614          *
615          * @param targetSurfaceControl Handle to the [SurfaceControlCompat] where the buffer is
616          *   presented. This can be used to configure various properties of the
617          *   [SurfaceControlCompat] like z-ordering or visibility with the corresponding
618          *   [SurfaceControlCompat.Transaction].
619          * @param transaction Current [SurfaceControlCompat.Transaction] to apply updated buffered
620          *   content to the front buffered layer.
621          * @param frameBuffer The buffer that has been rendered into and is ready to be displayed.
622          *   The [HardwareBuffer] backing this [FrameBuffer] is already configured to be presented
623          *   for the targetSurfaceControl. That is [SurfaceControlCompat.Transaction.setBuffer] is
624          *   already invoked with the given [HardwareBuffer] and optional [SyncFenceCompat] instance
625          *   before this method is invoked.
626          * @param syncFence Optional [SyncFenceCompat] is used to determine when rendering is done
627          *   and reflected within the given frameBuffer.
628          */
629         @WorkerThread
630         fun onDrawComplete(
631             targetSurfaceControl: SurfaceControlCompat,
632             transaction: SurfaceControlCompat.Transaction,
633             frameBuffer: FrameBuffer,
634             syncFence: SyncFenceCompat?
635         ) {
636             // NO-OP
637         }
638 
639         /**
640          * Optional callback invoked the thread backed by the [GLRenderer] when the provided
641          * framebuffer is released. That is the given [FrameBuffer] instance is no longer being
642          * presented and is not visible.
643          *
644          * @param frameBuffer The buffer that is no longer being presented and has returned to the
645          *   buffer allocation pool
646          * @param releaseFence Optional fence that must be waited upon before the [FrameBuffer] can
647          *   be reused. The framework will invoke this callback early to improve performance and
648          *   signal the fence when it is ready to be re-used.
649          */
650         @WorkerThread
651         fun onBufferReleased(frameBuffer: FrameBuffer, releaseFence: SyncFenceCompat?) {
652             // NO-OP
653         }
654     }
655 
656     /**
657      * Provider interface used to delegate creation and potential lifecycle callbacks associated
658      * with the corresponding [SurfaceControlCompat] instance
659      */
660     internal interface SurfaceControlProvider {
661 
662         /**
663          * Request a [SurfaceControlCompat] to be created and invokes the corresponding callback
664          * when the created [SurfaceControlCompat] instance is ready for consumption
665          */
666         fun createSurfaceControl(callback: Callback)
667 
668         /** Release resources associated with the [SurfaceControlProvider] */
669         fun release()
670 
671         /**
672          * Callbacks invoked by the [SurfaceControlProvider] to consumers of the created
673          * [SurfaceControlCompat] instance
674          */
675         interface Callback {
676 
677             /**
678              * Callback invoked when resources associated with the created [SurfaceControlCompat]
679              * instance should be destroyed
680              */
681             fun onSurfaceControlDestroyed()
682 
683             /**
684              * Callback invoked when the [SurfaceControlCompat] is created. This includes the
685              * logical width/height as well as a [BufferTransformer] instance that provides the
686              * metadata necessary to pre-rotate content
687              */
688             fun onSurfaceControlCreated(
689                 surfaceControl: SurfaceControlCompat,
690                 width: Int,
691                 height: Int,
692                 bufferTransformer: BufferTransformer,
693                 inverseTransform: Int
694             )
695 
696             /**
697              * Requests the consumer of the [SurfaceControlCompat] instance to render content to be
698              * presented by the [SurfaceControlCompat] instance and invoke a callback when rendering
699              * is complete.
700              */
701             fun requestRender(renderComplete: Runnable? = null)
702         }
703     }
704 
705     /**
706      * Default [SurfaceControlProvider] instance that returns the dependencies given to it to the
707      * consumer of the [SurfaceControlCompat]
708      */
709     internal class DefaultSurfaceControlProvider(
710         private val surfaceControl: SurfaceControlCompat,
711         private val width: Int,
712         private val height: Int,
713         private val transformHint: Int,
714     ) : SurfaceControlProvider {
715 
716         private val bufferTransformer = BufferTransformer()
717 
718         private var mSurfaceControlCallback: SurfaceControlProvider.Callback? = null
719 
720         override fun createSurfaceControl(callback: SurfaceControlProvider.Callback) {
721             val inverse = bufferTransformer.invertBufferTransform(transformHint)
722             bufferTransformer.computeTransform(width, height, inverse)
723             callback.onSurfaceControlCreated(
724                 surfaceControl,
725                 width,
726                 height,
727                 bufferTransformer,
728                 inverse
729             )
730             mSurfaceControlCallback = callback
731         }
732 
733         override fun release() {
734             // NO-OP
735         }
736     }
737 
738     /**
739      * [SurfaceControlProvider] instance that creates a [SurfaceControlCompat] instance with the
740      * provided SurfaceView as the parent of the created [SurfaceControlCompat]. This implementation
741      * handles all lifecycle callbacks associated with the underlying SurfaceHolder.Callback
742      * attached to the holder on the given SurfaceView.
743      */
744     internal class SurfaceViewProvider(private var surfaceView: SurfaceView?) :
745         SurfaceControlProvider {
746         private val mTransformResolver = BufferTransformHintResolver()
747 
748         private var mSurfaceControl: SurfaceControlCompat? = null
749         private var mSurfaceHolderCallback: SurfaceHolder.Callback2? = null
750         private var mSurfaceControlCallback: SurfaceControlProvider.Callback? = null
751 
752         internal fun createSurfaceControl(
753             surfaceView: SurfaceView,
754             callback: SurfaceControlProvider.Callback
755         ) {
756             // Destroy previously created SurfaceControl as we are creating a new instance
757             callback.onSurfaceControlDestroyed()
758 
759             val width = surfaceView.width
760             val height = surfaceView.height
761             val transformHint = mTransformResolver.getBufferTransformHint(surfaceView)
762             val bufferTransformer = BufferTransformer()
763             val inverse = bufferTransformer.invertBufferTransform(transformHint)
764             bufferTransformer.computeTransform(surfaceView.width, surfaceView.height, inverse)
765             val surfaceControl =
766                 SurfaceControlCompat.Builder()
767                     .setName("GLFrameBufferRendererTarget")
768                     .setParent(surfaceView)
769                     .build()
770 
771             callback.onSurfaceControlCreated(
772                 surfaceControl,
773                 width,
774                 height,
775                 bufferTransformer,
776                 inverse
777             )
778 
779             mSurfaceControl = surfaceControl
780             mSurfaceControlCallback = callback
781         }
782 
783         override fun createSurfaceControl(callback: SurfaceControlProvider.Callback) {
784             surfaceView?.let { target ->
785                 val surfaceHolderCallback =
786                     object : SurfaceHolder.Callback2 {
787                         override fun surfaceCreated(holder: SurfaceHolder) {
788                             // NO-OP wait for surfaceChanged callback
789                         }
790 
791                         override fun surfaceChanged(
792                             holder: SurfaceHolder,
793                             surfaceFormat: Int,
794                             width: Int,
795                             height: Int
796                         ) {
797                             if (width > 0 && height > 0) {
798                                 createSurfaceControl(target, callback)
799                             } else {
800                                 Log.w(
801                                     TAG,
802                                     "Invalid dimensions provided, width and height must be > 0. " +
803                                         "width: $width height: $height"
804                                 )
805                             }
806                         }
807 
808                         override fun surfaceDestroyed(p0: SurfaceHolder) {
809                             callback.onSurfaceControlDestroyed()
810                         }
811 
812                         override fun surfaceRedrawNeeded(p0: SurfaceHolder) {
813                             val latch = CountDownLatch(1)
814                             callback.requestRender { latch.countDown() }
815                             latch.await()
816                         }
817 
818                         override fun surfaceRedrawNeededAsync(
819                             holder: SurfaceHolder,
820                             drawingFinished: Runnable
821                         ) {
822                             callback.requestRender(drawingFinished)
823                         }
824                     }
825                 val holder = target.holder
826                 holder.addCallback(surfaceHolderCallback)
827                 if (holder.surface != null && holder.surface.isValid) {
828                     if (target.width > 0 && target.height > 0) {
829                         createSurfaceControl(target, callback)
830                     }
831                 }
832                 mSurfaceHolderCallback = surfaceHolderCallback
833             }
834         }
835 
836         override fun release() {
837             surfaceView?.holder?.removeCallback(mSurfaceHolderCallback)
838             surfaceView = null
839         }
840     }
841 
842     internal companion object {
843         internal val TAG = "GLFrameBufferRenderer"
844     }
845 }
846