SurfaceViewRenderLayer.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.lowlatency

import android.annotation.SuppressLint
import android.os.Build
import android.util.Log
import android.view.SurfaceHolder
import android.view.SurfaceView
import androidx.annotation.RequiresApi
import androidx.graphics.opengl.GLRenderer
import androidx.graphics.opengl.egl.EGLManager
import androidx.graphics.opengl.egl.EGLSpec
import androidx.graphics.surface.SurfaceControlCompat
import java.util.Collections

/**
 * [ParentRenderLayer] instance that leverages a [SurfaceView]'s [SurfaceControlCompat] as the
 * parent [SurfaceControlCompat] for the front and double buffered layers
 */
@RequiresApi(Build.VERSION_CODES.Q)
internal class SurfaceViewRenderLayer<T>(
    private val surfaceView: SurfaceView
) : ParentRenderLayer<T> {

    private var mLayerCallback: ParentRenderLayer.Callback<T>? = null
    private var mFrameBufferRenderer: FrameBufferRenderer? = null
    private var mRenderTarget: GLRenderer.RenderTarget? = null
    private var mParentSurfaceControl: SurfaceControlCompat? = null
    private val mBufferTransform = BufferTransformer()

    private val mTransformResolver = BufferTransformHintResolver()

    private var transformHint = BufferTransformHintResolver.UNKNOWN_TRANSFORM
    private var inverse = BufferTransformHintResolver.UNKNOWN_TRANSFORM
    init {
        surfaceView.holder.addCallback(object : SurfaceHolder.Callback {

            override fun surfaceCreated(holder: SurfaceHolder) {
                // NO-OP wait on surfaceChanged callback
            }

            override fun surfaceChanged(
                holder: SurfaceHolder,
                format: Int,
                width: Int,
                height: Int
            ) {
                transformHint = getBufferTransformHint()
                inverse = mBufferTransform.invertBufferTransform(transformHint)
                mBufferTransform.computeTransform(width, height, inverse)
                mParentSurfaceControl?.release()
                mLayerCallback?.onSizeChanged(width, height)
                mParentSurfaceControl = createDoubleBufferedSurfaceControl()
            }

            override fun surfaceDestroyed(p0: SurfaceHolder) {
                mLayerCallback?.onLayerDestroyed()
            }
        })
    }

    override fun getBufferTransformHint(): Int {
        return mTransformResolver.getBufferTransformHint(surfaceView)
    }

    override fun buildReparentTransaction(
        child: SurfaceControlCompat,
        transaction: SurfaceControlCompat.Transaction
    ) {
        transaction.reparent(child, mParentSurfaceControl)
    }

    override fun setParent(builder: SurfaceControlCompat.Builder) {
        builder.setParent(surfaceView)
    }

    @RequiresApi(Build.VERSION_CODES.TIRAMISU)
    override fun createRenderTarget(
        renderer: GLRenderer,
        renderLayerCallback: GLFrontBufferedRenderer.Callback<T>
    ): GLRenderer.RenderTarget {
        var params: Collection<T>? = null
        val frameBufferRenderer = FrameBufferRenderer(
            object : FrameBufferRenderer.RenderCallback {

                override fun obtainFrameBuffer(egl: EGLSpec): FrameBuffer =
                    mLayerCallback?.getFrameBufferPool()?.obtain(egl)
                        ?: throw IllegalArgumentException("No FrameBufferPool available")

                override fun onDraw(eglManager: EGLManager) {
                    renderLayerCallback.onDrawDoubleBufferedLayer(
                        eglManager,
                        mBufferTransform.glWidth,
                        mBufferTransform.glHeight,
                        mBufferTransform.transform,
                        params ?: Collections.emptyList()
                    )
                }

                @SuppressLint("WrongConstant")
                @RequiresApi(Build.VERSION_CODES.TIRAMISU)
                override fun onDrawComplete(
                    frameBuffer: FrameBuffer,
                    syncFenceCompat: SyncFenceCompat?
                ) {
                    val frontBufferedLayerSurfaceControl = mLayerCallback
                        ?.getFrontBufferedLayerSurfaceControl()
                    val sc = mParentSurfaceControl
                    // At this point the parentSurfaceControl should already be created
                    // in the surfaceChanged callback, however, if for whatever reason this
                    // was not the case, create the double buffered SurfaceControl now and cache
                    // it
                        ?: createDoubleBufferedSurfaceControl().also {
                            mParentSurfaceControl = it
                        }
                    if (frontBufferedLayerSurfaceControl != null) {
                        val transaction = SurfaceControlCompat.Transaction()
                            .setVisibility(frontBufferedLayerSurfaceControl, false)
                            .setVisibility(sc, true)
                            .setBuffer(sc, frameBuffer.hardwareBuffer, syncFenceCompat) {
                                mLayerCallback?.getFrameBufferPool()?.release(frameBuffer)
                            }
                        if (transformHint != BufferTransformHintResolver.UNKNOWN_TRANSFORM) {
                            transaction.setBufferTransform(sc, inverse)
                        }

                        renderLayerCallback.onDoubleBufferedLayerRenderComplete(
                            frontBufferedLayerSurfaceControl,
                            transaction
                        )
                        transaction.commit()
                    } else {
                        Log.e(
                            TAG, "Error, no front buffered SurfaceControl available to " +
                                "synchronize transaction with"
                        )
                    }
                }
            })
        val parentFrameBufferRenderer = WrapperFrameBufferRenderer<T>(frameBufferRenderer) {
            params = mLayerCallback?.obtainDoubleBufferedLayerParams()
            params != null
        }
        val renderTarget = renderer.attach(surfaceView, parentFrameBufferRenderer)
        mRenderTarget = renderTarget
        mFrameBufferRenderer = frameBufferRenderer
        return renderTarget
    }

    internal fun createDoubleBufferedSurfaceControl(): SurfaceControlCompat {
        val surfaceControl = SurfaceControlCompat.Builder()
            .setParent(surfaceView)
            .setName("DoubleBufferedLayer")
            .build()
        // SurfaceControl is not visible by default so make it visible right after creation
        SurfaceControlCompat.Transaction().setVisibility(surfaceControl, true).commit()
        return surfaceControl
    }

    override fun setParentLayerCallbacks(callback: ParentRenderLayer.Callback<T>?) {
        mLayerCallback = callback
    }

    override fun clear() {
        mFrameBufferRenderer?.clear()
        mRenderTarget?.requestRender()
    }

    @RequiresApi(Build.VERSION_CODES.TIRAMISU)
    override fun release(transaction: SurfaceControlCompat.Transaction) {
        mParentSurfaceControl?.let {
            transaction.reparent(it, null)
            it.release()
        }
        mParentSurfaceControl = null
    }

    internal companion object {
        internal const val TAG = "SurfaceViewRenderLayer"
    }
}