Add ability to make SyncFence from EGL

Bug: 217776226
Test: atest android.graphics.cts.EGL15Test#testEGL15AndroidNativeFence
Change-Id: I5e1356739831f63426521561c45e719fc5f419d9
This commit is contained in:
John Reck 2022-02-10 14:22:57 -05:00
parent 4f2645ae1b
commit 1ce46f750a
7 changed files with 311 additions and 98 deletions

View File

@ -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<android.hardware.SyncFence> 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 {

View File

@ -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.</p>
*
* @see <a href="https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCreateFence.html">
* VkFence</a>
* @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<SyncFence> CREATOR =
new Parcelable.Creator<SyncFence>() {
@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);
}

View File

@ -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",

View File

@ -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),

View File

@ -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 <nativehelper/JNIHelp.h>
#include <ui/Fence.h>
#include "core_jni_helpers.h"
#include "jni.h"
using namespace android;
template <typename T>
jlong toJlong(T* ptr) {
return static_cast<jlong>(reinterpret_cast<uintptr_t>(ptr));
}
template <typename T>
T* fromJlong(jlong jPtr) {
return reinterpret_cast<T*>(static_cast<uintptr_t>(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<Fence>(jPtr)->isValid();
}
static jint SyncFence_getFd(JNIEnv*, jobject, jlong jPtr) {
return fromJlong<Fence>(jPtr)->get();
}
static jboolean SyncFence_wait(JNIEnv* env, jobject, jlong jPtr, jlong timeoutNanos) {
Fence* fence = fromJlong<Fence>(jPtr);
int err = fence->wait(timeoutNanos);
return err == OK;
}
static jlong SyncFence_getSignalTime(JNIEnv* env, jobject, jlong jPtr) {
return fromJlong<Fence>(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;
}

View File

@ -37,25 +37,12 @@
#include <ui/ANativeObjectBase.h>
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, "<init>", "(J)V");
eglcontextConstructor = _env->GetMethodID(eglcontextClass, "<init>", "(J)V");
eglsurfaceConstructor = _env->GetMethodID(eglsurfaceClass, "<init>", "(J)V");
eglconfigConstructor = _env->GetMethodID(eglconfigClass, "<init>", "(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<void*>(_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<jlong>(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)

View File

@ -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 <a href="https://www.khronos.org/registry/EGL/extensions/ANDROID/EGL_ANDROID_native_fence_sync.txt">
* EGL_ANDROID_native_fence_sync</a> 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 <sync> is not a valid sync object for <display>,
* 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 <sync> is
* EGL_NO_NATIVE_FENCE_FD_ANDROID, an {@link SyncFence#isValid() invalid} SyncFence is
* returned and an EGL_BAD_PARAMETER error is generated.
* * If <display> does not match the display passed to eglCreateSync
* when <sync> 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);
}