diff --git a/core/api/current.txt b/core/api/current.txt
index 2c085b4241e6..d35fb69547d8 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -17108,12 +17108,17 @@ package android.hardware {
field public static final int MICROPHONE = 1; // 0x1
}
- public final class SyncFence implements java.io.Closeable android.os.Parcelable {
- method public void close() throws java.io.IOException;
+ public final class SyncFence implements java.lang.AutoCloseable android.os.Parcelable {
+ method public boolean await(@NonNull java.time.Duration);
+ method public boolean awaitForever();
+ method public void close();
method public int describeContents();
+ method public long getSignalTime();
method public boolean isValid();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator CREATOR;
+ field public static final long SIGNAL_TIME_INVALID = -1L; // 0xffffffffffffffffL
+ field public static final long SIGNAL_TIME_PENDING = 9223372036854775807L; // 0x7fffffffffffffffL
}
public final class TriggerEvent {
@@ -27843,12 +27848,17 @@ package android.opengl {
public class EGLExt {
ctor public EGLExt();
+ method @NonNull public static android.hardware.SyncFence eglDupNativeFenceFDANDROID(@NonNull android.opengl.EGLDisplay, @NonNull android.opengl.EGLSync);
method public static boolean eglPresentationTimeANDROID(android.opengl.EGLDisplay, android.opengl.EGLSurface, long);
field public static final int EGL_CONTEXT_FLAGS_KHR = 12540; // 0x30fc
field public static final int EGL_CONTEXT_MAJOR_VERSION_KHR = 12440; // 0x3098
field public static final int EGL_CONTEXT_MINOR_VERSION_KHR = 12539; // 0x30fb
+ field public static final int EGL_NO_NATIVE_FENCE_FD_ANDROID = -1; // 0xffffffff
field public static final int EGL_OPENGL_ES3_BIT_KHR = 64; // 0x40
field public static final int EGL_RECORDABLE_ANDROID = 12610; // 0x3142
+ field public static final int EGL_SYNC_NATIVE_FENCE_ANDROID = 12612; // 0x3144
+ field public static final int EGL_SYNC_NATIVE_FENCE_FD_ANDROID = 12613; // 0x3145
+ field public static final int EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID = 12614; // 0x3146
}
public class EGLImage extends android.opengl.EGLObjectHandle {
diff --git a/core/java/android/hardware/SyncFence.java b/core/java/android/hardware/SyncFence.java
index b0a6f51c2a39..693cda7720e4 100644
--- a/core/java/android/hardware/SyncFence.java
+++ b/core/java/android/hardware/SyncFence.java
@@ -17,12 +17,17 @@
package android.hardware;
import android.annotation.NonNull;
+import android.opengl.EGLDisplay;
+import android.opengl.EGLSync;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
-import java.io.Closeable;
+import libcore.util.NativeAllocationRegistry;
+
+import java.io.FileDescriptor;
import java.io.IOException;
+import java.time.Duration;
/**
* A SyncFence represents a synchronization primitive which signals when hardware buffers have
@@ -34,19 +39,57 @@ import java.io.IOException;
* Once the fence signals, then the backing storage for the framebuffer may be safely read from,
* such as for display or for media encoding.
*
- * @see
- * VkFence
+ * @see android.opengl.EGLExt#eglDupNativeFenceFDANDROID(EGLDisplay, EGLSync)
+ * @see android.media.Image#getFence()
*/
-public final class SyncFence implements Closeable, Parcelable {
- private static final String TAG = "SyncFence";
+public final class SyncFence implements AutoCloseable, Parcelable {
/**
- * Wrapped {@link android.os.ParcelFileDescriptor}.
+ * An invalid signal time. Represents either the signal time for a SyncFence that isn't valid
+ * (that is, {@link #isValid()} is false), or if an error occurred while attempting to retrieve
+ * the signal time.
*/
- private ParcelFileDescriptor mWrapped;
+ public static final long SIGNAL_TIME_INVALID = -1;
+
+ /**
+ * A pending signal time. This is equivalent to the max value of a long, representing an
+ * infinitely far point in the future.
+ */
+ public static final long SIGNAL_TIME_PENDING = Long.MAX_VALUE;
+
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createNonmalloced(SyncFence.class.getClassLoader(),
+ nGetDestructor(), 4);
+
+ private long mNativePtr;
+
+ // The destructor for this object
+ // This is also used as our internal lock object. Although SyncFence doesn't claim to be
+ // thread-safe, the cost of doing so to avoid issues around double-close or similar issues
+ // is well worth making.
+ private final Runnable mCloser;
private SyncFence(@NonNull ParcelFileDescriptor wrapped) {
- mWrapped = wrapped;
+ mNativePtr = nCreate(wrapped.detachFd());
+ mCloser = sRegistry.registerNativeAllocation(this, mNativePtr);
+ }
+
+ private SyncFence(@NonNull Parcel parcel) {
+ boolean valid = parcel.readBoolean();
+ FileDescriptor fileDescriptor = null;
+ if (valid) {
+ fileDescriptor = parcel.readRawFileDescriptor();
+ }
+ if (fileDescriptor != null) {
+ mNativePtr = nCreate(fileDescriptor.getInt$());
+ mCloser = sRegistry.registerNativeAllocation(this, mNativePtr);
+ } else {
+ mCloser = () -> {};
+ }
+ }
+
+ private SyncFence() {
+ mCloser = () -> {};
}
/***
@@ -56,7 +99,7 @@ public final class SyncFence implements Closeable, Parcelable {
* @hide
*/
public static @NonNull SyncFence createEmpty() {
- return new SyncFence(ParcelFileDescriptor.adoptFd(-1));
+ return new SyncFence();
}
/**
@@ -75,7 +118,13 @@ public final class SyncFence implements Closeable, Parcelable {
* @hide
*/
public @NonNull ParcelFileDescriptor getFdDup() throws IOException {
- return mWrapped.dup();
+ synchronized (mCloser) {
+ final int fd = mNativePtr != 0 ? nGetFd(mNativePtr) : -1;
+ if (fd == -1) {
+ throw new IllegalStateException("Cannot dup the FD of an invalid SyncFence");
+ }
+ return ParcelFileDescriptor.fromFd(fd);
+ }
}
/**
@@ -85,29 +134,84 @@ public final class SyncFence implements Closeable, Parcelable {
* {@code false} otherwise.
*/
public boolean isValid() {
- return mWrapped.getFileDescriptor().valid();
+ synchronized (mCloser) {
+ return mNativePtr != 0 && nIsValid(mNativePtr);
+ }
+ }
+
+ /**
+ * Waits for a SyncFence to signal for up to the timeout duration.
+ *
+ * An invalid SyncFence, that is if {@link #isValid()} is false, is treated equivalently
+ * to a SyncFence that has already signaled. That is, wait() will immediately return true.
+ *
+ * @param timeout The timeout duration. If the duration is negative, then this waits forever.
+ * @return true if the fence signaled or isn't valid, false otherwise.
+ */
+ public boolean await(@NonNull Duration timeout) {
+ final long timeoutNanos;
+ if (timeout.isNegative()) {
+ timeoutNanos = -1;
+ } else {
+ timeoutNanos = timeout.toNanos();
+ }
+ return await(timeoutNanos);
+ }
+
+ /**
+ * Waits forever for a SyncFence to signal.
+ *
+ * An invalid SyncFence, that is if {@link #isValid()} is false, is treated equivalently
+ * to a SyncFence that has already signaled. That is, wait() will immediately return true.
+ *
+ * @return true if the fence signaled or isn't valid, false otherwise.
+ */
+ public boolean awaitForever() {
+ return await(-1);
+ }
+
+ private boolean await(long timeoutNanos) {
+ synchronized (mCloser) {
+ return mNativePtr != 0 && nWait(mNativePtr, timeoutNanos);
+ }
+ }
+
+ /**
+ * Returns the time that the fence signaled in the CLOCK_MONOTONIC time domain.
+ *
+ * If the fence isn't valid, that is if {@link #isValid()} is false, then this returns
+ * {@link #SIGNAL_TIME_INVALID}. Similarly, if an error occurs while trying to access the
+ * signal time, then {@link #SIGNAL_TIME_INVALID} is also returned.
+ *
+ * If the fence hasn't yet signaled, then {@link #SIGNAL_TIME_PENDING} is returned.
+ *
+ * @return The time the fence signaled, {@link #SIGNAL_TIME_INVALID} if there's an error,
+ * or {@link #SIGNAL_TIME_PENDING} if the fence hasn't signaled yet.
+ */
+ public long getSignalTime() {
+ synchronized (mCloser) {
+ return mNativePtr != 0 ? nGetSignalTime(mNativePtr) : SIGNAL_TIME_INVALID;
+ }
}
/**
* Close the SyncFence. This implementation closes the underlying OS resources allocated
* this stream.
- *
- * @throws IOException If an error occurs attempting to close this SyncFence.
*/
@Override
- public void close() throws IOException {
- if (mWrapped != null) {
- try {
- mWrapped.close();
- } finally {
- // success
+ public void close() {
+ synchronized (mCloser) {
+ if (mNativePtr == 0) {
+ return;
}
+ mNativePtr = 0;
+ mCloser.run();
}
}
@Override
public int describeContents() {
- return mWrapped.describeContents();
+ return CONTENTS_FILE_DESCRIPTOR;
}
/**
@@ -119,23 +223,36 @@ public final class SyncFence implements Closeable, Parcelable {
*/
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
- try {
- mWrapped.writeToParcel(out, flags);
- } finally {
- // success
+ synchronized (mCloser) {
+ final int fd = mNativePtr != 0 ? nGetFd(mNativePtr) : -1;
+ if (fd == -1) {
+ out.writeBoolean(false);
+ } else {
+ out.writeBoolean(true);
+ FileDescriptor temp = new FileDescriptor();
+ temp.setInt$(fd);
+ out.writeFileDescriptor(temp);
+ }
}
}
public static final @NonNull Parcelable.Creator CREATOR =
new Parcelable.Creator() {
- @Override
- public SyncFence createFromParcel(Parcel in) {
- return new SyncFence(ParcelFileDescriptor.CREATOR.createFromParcel(in));
- }
+ @Override
+ public SyncFence createFromParcel(Parcel in) {
+ return new SyncFence(in);
+ }
- @Override
- public SyncFence[] newArray(int size) {
- return new SyncFence[size];
- }
- };
+ @Override
+ public SyncFence[] newArray(int size) {
+ return new SyncFence[size];
+ }
+ };
+
+ private static native long nGetDestructor();
+ private static native long nCreate(int fd);
+ private static native boolean nIsValid(long nPtr);
+ private static native int nGetFd(long nPtr);
+ private static native boolean nWait(long nPtr, long timeout);
+ private static native long nGetSignalTime(long nPtr);
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 955f46b977b7..0a0ddba4f325 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -188,6 +188,7 @@ cc_library_shared {
"android_hardware_HardwareBuffer.cpp",
"android_hardware_SensorManager.cpp",
"android_hardware_SerialPort.cpp",
+ "android_hardware_SyncFence.cpp",
"android_hardware_UsbDevice.cpp",
"android_hardware_UsbDeviceConnection.cpp",
"android_hardware_UsbRequest.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index cde71cf6b30f..eedf7fa34a04 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -82,6 +82,7 @@ extern int register_android_hardware_display_DisplayManagerGlobal(JNIEnv* env);
extern int register_android_hardware_HardwareBuffer(JNIEnv *env);
extern int register_android_hardware_SensorManager(JNIEnv *env);
extern int register_android_hardware_SerialPort(JNIEnv *env);
+extern int register_android_hardware_SyncFence(JNIEnv* env);
extern int register_android_hardware_UsbDevice(JNIEnv *env);
extern int register_android_hardware_UsbDeviceConnection(JNIEnv *env);
extern int register_android_hardware_UsbRequest(JNIEnv *env);
@@ -1601,6 +1602,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_hardware_HardwareBuffer),
REG_JNI(register_android_hardware_SensorManager),
REG_JNI(register_android_hardware_SerialPort),
+ REG_JNI(register_android_hardware_SyncFence),
REG_JNI(register_android_hardware_UsbDevice),
REG_JNI(register_android_hardware_UsbDeviceConnection),
REG_JNI(register_android_hardware_UsbRequest),
diff --git a/core/jni/android_hardware_SyncFence.cpp b/core/jni/android_hardware_SyncFence.cpp
new file mode 100644
index 000000000000..b99665346f25
--- /dev/null
+++ b/core/jni/android_hardware_SyncFence.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "SyncFence"
+
+#include
+#include
+
+#include "core_jni_helpers.h"
+#include "jni.h"
+
+using namespace android;
+
+template
+jlong toJlong(T* ptr) {
+ return static_cast(reinterpret_cast(ptr));
+}
+
+template
+T* fromJlong(jlong jPtr) {
+ return reinterpret_cast(static_cast(jPtr));
+}
+
+static void destroyFence(Fence* fence) {
+ fence->decStrong(0);
+}
+
+static jlong SyncFence_getDestructor(JNIEnv*, jobject) {
+ return toJlong(&destroyFence);
+}
+
+static jlong SyncFence_create(JNIEnv*, jobject, int fd) {
+ Fence* fence = new Fence(fd);
+ fence->incStrong(0);
+ return toJlong(fence);
+}
+
+static jboolean SyncFence_isValid(JNIEnv*, jobject, jlong jPtr) {
+ return fromJlong(jPtr)->isValid();
+}
+
+static jint SyncFence_getFd(JNIEnv*, jobject, jlong jPtr) {
+ return fromJlong(jPtr)->get();
+}
+
+static jboolean SyncFence_wait(JNIEnv* env, jobject, jlong jPtr, jlong timeoutNanos) {
+ Fence* fence = fromJlong(jPtr);
+ int err = fence->wait(timeoutNanos);
+ return err == OK;
+}
+
+static jlong SyncFence_getSignalTime(JNIEnv* env, jobject, jlong jPtr) {
+ return fromJlong(jPtr)->getSignalTime();
+}
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "android/hardware/SyncFence";
+
+// clang-format off
+static const JNINativeMethod gMethods[] = {
+ { "nGetDestructor", "()J", (void*) SyncFence_getDestructor },
+ { "nCreate", "(I)J", (void*) SyncFence_create },
+ { "nIsValid", "(J)Z", (void*) SyncFence_isValid },
+ { "nGetFd", "(J)I", (void*) SyncFence_getFd },
+ { "nWait", "(JJ)Z", (void*) SyncFence_wait },
+ { "nGetSignalTime", "(J)J", (void*) SyncFence_getSignalTime },
+};
+// clang-format on
+
+int register_android_hardware_SyncFence(JNIEnv* env) {
+ int err = RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
+ return err;
+}
\ No newline at end of file
diff --git a/core/jni/android_opengl_EGLExt.cpp b/core/jni/android_opengl_EGLExt.cpp
index 1758807304bf..cdc985273472 100644
--- a/core/jni/android_opengl_EGLExt.cpp
+++ b/core/jni/android_opengl_EGLExt.cpp
@@ -37,25 +37,12 @@
#include
static jclass egldisplayClass;
-static jclass eglcontextClass;
static jclass eglsurfaceClass;
-static jclass eglconfigClass;
+static jclass eglsyncClass;
static jmethodID egldisplayGetHandleID;
-static jmethodID eglcontextGetHandleID;
static jmethodID eglsurfaceGetHandleID;
-static jmethodID eglconfigGetHandleID;
-
-static jmethodID egldisplayConstructor;
-static jmethodID eglcontextConstructor;
-static jmethodID eglsurfaceConstructor;
-static jmethodID eglconfigConstructor;
-
-static jobject eglNoContextObject;
-static jobject eglNoDisplayObject;
-static jobject eglNoSurfaceObject;
-
-
+static jmethodID eglsyncGetHandleID;
/* Cache method IDs each time the class is loaded. */
@@ -64,37 +51,14 @@ nativeClassInit(JNIEnv *_env, jclass glImplClass)
{
jclass egldisplayClassLocal = _env->FindClass("android/opengl/EGLDisplay");
egldisplayClass = (jclass) _env->NewGlobalRef(egldisplayClassLocal);
- jclass eglcontextClassLocal = _env->FindClass("android/opengl/EGLContext");
- eglcontextClass = (jclass) _env->NewGlobalRef(eglcontextClassLocal);
jclass eglsurfaceClassLocal = _env->FindClass("android/opengl/EGLSurface");
eglsurfaceClass = (jclass) _env->NewGlobalRef(eglsurfaceClassLocal);
- jclass eglconfigClassLocal = _env->FindClass("android/opengl/EGLConfig");
- eglconfigClass = (jclass) _env->NewGlobalRef(eglconfigClassLocal);
+ jclass eglsyncClassLocal = _env->FindClass("android/opengl/EGLSync");
+ eglsyncClass = (jclass)_env->NewGlobalRef(eglsyncClassLocal);
egldisplayGetHandleID = _env->GetMethodID(egldisplayClass, "getNativeHandle", "()J");
- eglcontextGetHandleID = _env->GetMethodID(eglcontextClass, "getNativeHandle", "()J");
eglsurfaceGetHandleID = _env->GetMethodID(eglsurfaceClass, "getNativeHandle", "()J");
- eglconfigGetHandleID = _env->GetMethodID(eglconfigClass, "getNativeHandle", "()J");
-
-
- egldisplayConstructor = _env->GetMethodID(egldisplayClass, "", "(J)V");
- eglcontextConstructor = _env->GetMethodID(eglcontextClass, "", "(J)V");
- eglsurfaceConstructor = _env->GetMethodID(eglsurfaceClass, "", "(J)V");
- eglconfigConstructor = _env->GetMethodID(eglconfigClass, "", "(J)V");
-
-
- jclass eglClass = _env->FindClass("android/opengl/EGL14");
- jfieldID noContextFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_CONTEXT", "Landroid/opengl/EGLContext;");
- jobject localeglNoContextObject = _env->GetStaticObjectField(eglClass, noContextFieldID);
- eglNoContextObject = _env->NewGlobalRef(localeglNoContextObject);
-
- jfieldID noDisplayFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_DISPLAY", "Landroid/opengl/EGLDisplay;");
- jobject localeglNoDisplayObject = _env->GetStaticObjectField(eglClass, noDisplayFieldID);
- eglNoDisplayObject = _env->NewGlobalRef(localeglNoDisplayObject);
-
- jfieldID noSurfaceFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_SURFACE", "Landroid/opengl/EGLSurface;");
- jobject localeglNoSurfaceObject = _env->GetStaticObjectField(eglClass, noSurfaceFieldID);
- eglNoSurfaceObject = _env->NewGlobalRef(localeglNoSurfaceObject);
+ eglsyncGetHandleID = _env->GetMethodID(eglsyncClassLocal, "getNativeHandle", "()J");
}
static void *
@@ -108,26 +72,6 @@ fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) {
return reinterpret_cast(_env->CallLongMethod(obj, mid));
}
-static jobject
-toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void * handle) {
- if (cls == eglcontextClass &&
- (EGLContext)handle == EGL_NO_CONTEXT) {
- return eglNoContextObject;
- }
-
- if (cls == egldisplayClass &&
- (EGLDisplay)handle == EGL_NO_DISPLAY) {
- return eglNoDisplayObject;
- }
-
- if (cls == eglsurfaceClass &&
- (EGLSurface)handle == EGL_NO_SURFACE) {
- return eglNoSurfaceObject;
- }
-
- return _env->NewObject(cls, con, reinterpret_cast(handle));
-}
-
// --------------------------------------------------------------------------
/* EGLBoolean eglPresentationTimeANDROID ( EGLDisplay dpy, EGLSurface sur, EGLnsecsANDROID time ) */
static jboolean
@@ -145,11 +89,21 @@ android_eglPresentationTimeANDROID
return (jboolean)_returnValue;
}
+static jint android_eglDupNativeFenceFDANDROID(JNIEnv *env, jobject, jobject dpy, jobject sync) {
+ EGLDisplay dpy_native = (EGLDisplay)fromEGLHandle(env, egldisplayGetHandleID, dpy);
+ EGLSync sync_native = (EGLSync)fromEGLHandle(env, eglsyncGetHandleID, sync);
+
+ return eglDupNativeFenceFDANDROID(dpy_native, sync_native);
+}
+
static const char *classPathName = "android/opengl/EGLExt";
static const JNINativeMethod methods[] = {
-{"_nativeClassInit", "()V", (void*)nativeClassInit },
-{"eglPresentationTimeANDROID", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSurface;J)Z", (void *) android_eglPresentationTimeANDROID },
+ {"_nativeClassInit", "()V", (void *)nativeClassInit},
+ {"eglPresentationTimeANDROID", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSurface;J)Z",
+ (void *)android_eglPresentationTimeANDROID},
+ {"eglDupNativeFenceFDANDROIDImpl", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;)I",
+ (void *)android_eglDupNativeFenceFDANDROID},
};
int register_android_opengl_jni_EGLExt(JNIEnv *_env)
diff --git a/opengl/java/android/opengl/EGLExt.java b/opengl/java/android/opengl/EGLExt.java
index 74b64ead77bb..1570e0e22f50 100644
--- a/opengl/java/android/opengl/EGLExt.java
+++ b/opengl/java/android/opengl/EGLExt.java
@@ -18,6 +18,11 @@
package android.opengl;
+import android.annotation.NonNull;
+import android.hardware.SyncFence;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
/**
* EGL Extensions
*/
@@ -30,6 +35,12 @@ public class EGLExt {
public static final int EGL_OPENGL_ES3_BIT_KHR = 0x0040;
public static final int EGL_RECORDABLE_ANDROID = 0x3142;
+ // EGL_ANDROID_native_fence_sync
+ public static final int EGL_SYNC_NATIVE_FENCE_ANDROID = 0x3144;
+ public static final int EGL_SYNC_NATIVE_FENCE_FD_ANDROID = 0x3145;
+ public static final int EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID = 0x3146;
+ public static final int EGL_NO_NATIVE_FENCE_FD_ANDROID = -1;
+
native private static void _nativeClassInit();
static {
_nativeClassInit();
@@ -43,4 +54,33 @@ public class EGLExt {
long time
);
+ /**
+ * Retrieves the SyncFence for an EGLSync created with EGL_SYNC_NATIVE_FENCE_ANDROID
+ *
+ * See
+ * EGL_ANDROID_native_fence_sync extension for more details
+ * @param display The EGLDisplay connection
+ * @param sync The EGLSync to fetch the SyncFence from
+ * @return A SyncFence representing the native fence.
+ * * If is not a valid sync object for ,
+ * an {@link SyncFence#isValid() invalid} SyncFence is returned and an EGL_BAD_PARAMETER
+ * error is generated.
+ * * If the EGL_SYNC_NATIVE_FENCE_FD_ANDROID attribute of is
+ * EGL_NO_NATIVE_FENCE_FD_ANDROID, an {@link SyncFence#isValid() invalid} SyncFence is
+ * returned and an EGL_BAD_PARAMETER error is generated.
+ * * If does not match the display passed to eglCreateSync
+ * when was created, the behaviour is undefined.
+ */
+ public static @NonNull SyncFence eglDupNativeFenceFDANDROID(@NonNull EGLDisplay display,
+ @NonNull EGLSync sync) {
+ int fd = eglDupNativeFenceFDANDROIDImpl(display, sync);
+ Log.d("EGL", "eglDupNativeFence returned " + fd);
+ if (fd >= 0) {
+ return SyncFence.create(ParcelFileDescriptor.adoptFd(fd));
+ } else {
+ return SyncFence.createEmpty();
+ }
+ }
+
+ private static native int eglDupNativeFenceFDANDROIDImpl(EGLDisplay display, EGLSync sync);
}