FrontBufferSyncStrategy.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.hardware.HardwareBuffer
import android.opengl.GLES20
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.graphics.opengl.FrameBufferRenderer
import androidx.graphics.opengl.SyncStrategy
import androidx.graphics.opengl.egl.EGLSpec
import androidx.hardware.SyncFenceCompat

/**
 * [SyncStrategy] implementation that optimizes for front buffered rendering use cases.
 * More specifically this attempts to avoid unnecessary synchronization overhead
 * wherever possible.
 *
 * This will always provide a fence if the corresponding layer transitions from
 * an invisible to a visible state. If the layer is already visible and front
 * buffer usage flags are support on the device, then no fence is provided. If this
 * flag is not supported, then a fence is created to ensure contents
 * are flushed to the single buffer.
 *
 * @param usageFlags usage flags that describe the [HardwareBuffer] that is used as the destination
 * for rendering content within [FrameBufferRenderer]. The usage flags can be obtained via
 * [HardwareBuffer.getUsage] or by passing in the same flags from [HardwareBuffer.create]
 */
class FrontBufferSyncStrategy(
    usageFlags: Long
) : SyncStrategy {
    private val supportsFrontBufferUsage = (usageFlags and HardwareBuffer.USAGE_FRONT_BUFFER) != 0L
    private var mFrontBufferVisible: Boolean = false

    /**
     * Tells whether the corresponding front buffer layer is visible in its current state or not.
     * Utilize this to dictate when a [SyncFenceCompat] will be created when using
     * [createSyncFence].
     */
    var isVisible
        get() = mFrontBufferVisible
        set(visibility) {
            mFrontBufferVisible = visibility
        }

    /**
     * Creates a [SyncFenceCompat] based on various conditions.
     * If the layer is changing from invisible to visible, a fence is provided.
     * If the layer is already visible and front buffer usage flag is supported on the device, then
     * no fence is provided.
     * If front buffer usage is not supported, then a fence is created and destroyed to flush
     * contents to screen.
     */
    @RequiresApi(Build.VERSION_CODES.KITKAT)
    override fun createSyncFence(eglSpec: EGLSpec): SyncFenceCompat? {
        return if (!isVisible) {
            SyncFenceCompat.createNativeSyncFence()
        } else if (supportsFrontBufferUsage) {
            GLES20.glFlush()
            return null
        } else {
            val fence = SyncFenceCompat.createNativeSyncFence()
            fence.close()
            return null
        }
    }
}