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); }