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.hardware
18 
19 import android.opengl.EGL14
20 import android.opengl.EGL15
21 import android.opengl.GLES20
22 import android.os.Build
23 import androidx.annotation.RequiresApi
24 import androidx.graphics.lowlatency.FrontBufferUtils
25 import androidx.graphics.surface.SurfaceControlCompat
26 import androidx.opengl.EGLExt
27 import androidx.opengl.EGLSyncKHR
28 
29 /**
30  * A synchronization primitive which signals when hardware units have completed work on a particular
31  * resource. They initially start in an unsignaled state and make a one-time transaction to either a
32  * signaled or error state.
33  *
34  * [SyncFenceCompat] is a presentation fence used in combination with
35  * [SurfaceControlCompat.Transaction.setBuffer]. Note that depending on API level, this will utilize
36  * either [android.hardware.SyncFence] or a compatibility implementation.
37  */
38 class SyncFenceCompat : AutoCloseable {
39     internal val mImpl: SyncFenceImpl
40 
41     companion object {
42         /**
43          * Creates a native synchronization fence from an EGLSync object.
44          *
45          * @throws IllegalStateException if EGL dependencies cannot be resolved
46          */
47         @JvmStatic
createNativeSyncFencenull48         fun createNativeSyncFence(): SyncFenceCompat {
49             val usePlatformSyncFence =
50                 !FrontBufferUtils.UseCompatSurfaceControl &&
51                     Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
52             return if (usePlatformSyncFence) {
53                 SyncFenceCompatVerificationHelper.createSyncFenceCompatV33()
54             } else {
55                 val display =
56                     EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)
57                         ?: throw IllegalStateException("No EGL Display available")
58                 val eglSync: EGLSyncKHR =
59                     EGLExt.eglCreateSyncKHR(display, EGLExt.EGL_SYNC_NATIVE_FENCE_ANDROID, null)
60                         ?: throw IllegalStateException("Unable to create sync object")
61                 GLES20.glFlush()
62 
63                 val syncFenceCompat = EGLExt.eglDupNativeFenceFDANDROID(display, eglSync)
64                 EGLExt.eglDestroySyncKHR(display, eglSync)
65 
66                 syncFenceCompat
67             }
68         }
69 
70         /**
71          * An invalid signal time. Represents either the signal time for a SyncFence that isn't
72          * valid (that is, [isValid] is `false`), or if an error occurred while attempting to
73          * retrieve the signal time.
74          */
75         const val SIGNAL_TIME_INVALID: Long = -1L
76 
77         /**
78          * A pending signal time. This is equivalent to the max value of a long, representing an
79          * infinitely far point in the future.
80          */
81         const val SIGNAL_TIME_PENDING: Long = Long.MAX_VALUE
82     }
83 
84     internal constructor(syncFence: SyncFenceV19) {
85         mImpl = syncFence
86     }
87 
88     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
89     internal constructor(syncFence: android.hardware.SyncFence) {
90         mImpl = SyncFenceV33(syncFence)
91     }
92 
93     /**
94      * Waits for a [SyncFenceCompat] to signal for up to the timeout duration
95      *
96      * @param timeoutNanos time in nanoseconds to wait for before timing out.
97      */
awaitnull98     fun await(timeoutNanos: Long): Boolean = mImpl.await(timeoutNanos)
99 
100     /** Waits forever for a [SyncFenceImpl] to signal */
101     fun awaitForever(): Boolean = mImpl.awaitForever()
102 
103     /** Close the [SyncFenceImpl] */
104     override fun close() {
105         mImpl.close()
106     }
107 
108     /**
109      * Returns the time that the fence signaled in the [CLOCK_MONOTONIC] time domain. This returns
110      * an instant, [SyncFenceCompat.SIGNAL_TIME_INVALID] if the SyncFence is invalid, and if the
111      * fence hasn't yet signaled, then [SyncFenceCompat.SIGNAL_TIME_PENDING] is returned.
112      */
113     @RequiresApi(Build.VERSION_CODES.O)
getSignalTimeNanosnull114     fun getSignalTimeNanos(): Long {
115         return mImpl.getSignalTimeNanos()
116     }
117 
118     /**
119      * Checks if the SyncFence object is valid.
120      *
121      * @return `true` if it is valid, `false` otherwise
122      */
isValidnull123     fun isValid() = mImpl.isValid()
124 }
125 
126 /** Helper class to avoid class verification failures */
127 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
128 internal class SyncFenceCompatVerificationHelper private constructor() {
129     companion object {
130         private val mEmptyAttributes = longArrayOf(EGL14.EGL_NONE.toLong())
131 
132         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
133         fun createSyncFenceCompatV33(): SyncFenceCompat {
134             val display = EGL14.eglGetCurrentDisplay()
135             if (display == EGL15.EGL_NO_DISPLAY) {
136                 throw RuntimeException("no EGL display")
137             }
138             val error = EGL14.eglGetError()
139             if (error != EGL14.EGL_SUCCESS) {
140                 throw RuntimeException("eglGetPlatformDisplay failed")
141             }
142 
143             val eglSync =
144                 EGL15.eglCreateSync(
145                     display,
146                     android.opengl.EGLExt.EGL_SYNC_NATIVE_FENCE_ANDROID,
147                     mEmptyAttributes,
148                     0
149                 )
150             GLES20.glFlush()
151             val syncFenceCompat =
152                 SyncFenceCompat(android.opengl.EGLExt.eglDupNativeFenceFDANDROID(display, eglSync))
153             EGL15.eglDestroySync(display, eglSync)
154 
155             return syncFenceCompat
156         }
157     }
158 }
159