Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1ce46f75 authored by John Reck's avatar John Reck
Browse files

Add ability to make SyncFence from EGL

Bug: 217776226
Test: atest android.graphics.cts.EGL15Test#testEGL15AndroidNativeFence
Change-Id: I5e1356739831f63426521561c45e719fc5f419d9
parent 4f2645ae
Loading
Loading
Loading
Loading
+12 −2
Original line number Diff line number Diff line
@@ -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 {
+150 −33
Original line number Diff line number Diff line
@@ -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 AutoCloseable, Parcelable {

    /**
     * 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.
     */
public final class SyncFence implements Closeable, Parcelable {
    private static final String TAG = "SyncFence";
    public static final long SIGNAL_TIME_INVALID = -1;

    /**
     * Wrapped {@link android.os.ParcelFileDescriptor}.
     * A pending signal time. This is equivalent to the max value of a long, representing an
     * infinitely far point in the future.
     */
    private ParcelFileDescriptor mWrapped;
    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,10 +223,16 @@ 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);
            }
        }
    }

@@ -130,7 +240,7 @@ public final class SyncFence implements Closeable, Parcelable {
            new Parcelable.Creator<SyncFence>() {
                @Override
                public SyncFence createFromParcel(Parcel in) {
            return new SyncFence(ParcelFileDescriptor.CREATOR.createFromParcel(in));
                    return new SyncFence(in);
                }

                @Override
@@ -138,4 +248,11 @@ public final class SyncFence implements Closeable, Parcelable {
                    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);
}
+1 −0
Original line number Diff line number Diff line
@@ -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",
+2 −0
Original line number Diff line number Diff line
@@ -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),
+89 −0
Original line number Diff line number Diff line
/*
 * 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;
}
 No newline at end of file
Loading