diff --git a/core/api/current.txt b/core/api/current.txt index d33a6ee87a90e..7a161be4b939c 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -49369,6 +49369,7 @@ package android.view { method @NonNull public android.view.SurfaceControl.Transaction setAlpha(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0, to=1.0) float); method @NonNull public android.view.SurfaceControl.Transaction setBuffer(@NonNull android.view.SurfaceControl, @Nullable android.hardware.HardwareBuffer); method @NonNull public android.view.SurfaceControl.Transaction setBuffer(@NonNull android.view.SurfaceControl, @Nullable android.hardware.HardwareBuffer, @Nullable android.hardware.SyncFence); + method @NonNull public android.view.SurfaceControl.Transaction setBuffer(@NonNull android.view.SurfaceControl, @Nullable android.hardware.HardwareBuffer, @Nullable android.hardware.SyncFence, @Nullable java.util.function.Consumer); method @NonNull public android.view.SurfaceControl.Transaction setBufferSize(@NonNull android.view.SurfaceControl, @IntRange(from=0) int, @IntRange(from=0) int); method @NonNull public android.view.SurfaceControl.Transaction setBufferTransform(@NonNull android.view.SurfaceControl, int); method @NonNull public android.view.SurfaceControl.Transaction setCrop(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect); diff --git a/core/java/android/hardware/SyncFence.java b/core/java/android/hardware/SyncFence.java index cd4bf78f0879a..166001347bd4c 100644 --- a/core/java/android/hardware/SyncFence.java +++ b/core/java/android/hardware/SyncFence.java @@ -24,6 +24,7 @@ import android.opengl.EGLSync; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; +import android.os.SystemClock; import libcore.util.NativeAllocationRegistry; @@ -105,6 +106,21 @@ public final class SyncFence implements AutoCloseable, Parcelable { } } + /** + * Creates a SyncFence from a libui Fence* + * DOES NOT TAKE AN ADDITIONAL REFERENCE, the caller must incref if it intends to retain + * ownership (eg, when using sp) + * @hide + */ + public SyncFence(long nativeFencePtr) { + mNativePtr = nativeFencePtr; + if (nativeFencePtr != 0) { + mCloser = sRegistry.registerNativeAllocation(this, mNativePtr); + } else { + mCloser = () -> {}; + } + } + private SyncFence() { mCloser = () -> {}; } @@ -194,7 +210,9 @@ public final class SyncFence implements AutoCloseable, Parcelable { } /** - * Returns the time that the fence signaled in the CLOCK_MONOTONIC time domain. + * Returns the time in nanoseconds that the fence signaled in the CLOCK_MONOTONIC time domain. + * This corresponds to {@link System#nanoTime()} but may also be compared to + * {@link SystemClock#uptimeMillis()} after adjusting for milliseconds vs. nanoseconds. * * 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 diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index f3c65eae8ba84..da582c5b4b2f6 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -83,6 +83,7 @@ import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; /** * Handle to an on-screen Surface managed by the system compositor. The SurfaceControl is @@ -213,7 +214,7 @@ public final class SurfaceControl implements Parcelable { private static native void nativeReparent(long transactionObj, long nativeObject, long newParentNativeObject); private static native void nativeSetBuffer(long transactionObj, long nativeObject, - HardwareBuffer buffer, long fencePtr); + HardwareBuffer buffer, long fencePtr, Consumer releaseCallback); private static native void nativeSetBufferTransform(long transactionObj, long nativeObject, int transform); private static native void nativeSetDataSpace(long transactionObj, long nativeObject, @@ -3744,18 +3745,62 @@ public final class SurfaceControl implements Parcelable { */ public @NonNull Transaction setBuffer(@NonNull SurfaceControl sc, @Nullable HardwareBuffer buffer, @Nullable SyncFence fence) { + return setBuffer(sc, buffer, fence, null); + } + + /** + * Updates the HardwareBuffer displayed for the SurfaceControl. + * + * Note that the buffer must be allocated with {@link HardwareBuffer#USAGE_COMPOSER_OVERLAY} + * as well as {@link HardwareBuffer#USAGE_GPU_SAMPLED_IMAGE} as the surface control might + * be composited using either an overlay or using the GPU. + * + * A presentation fence may be passed to improve performance by allowing the buffer + * to complete rendering while it is waiting for the transaction to be applied. + * For example, if the buffer is being produced by rendering with OpenGL ES then + * a fence created with + * {@link android.opengl.EGLExt#eglDupNativeFenceFDANDROID(EGLDisplay, EGLSync)} can be + * used to allow the GPU rendering to be concurrent with the transaction. The compositor + * will wait for the fence to be signaled before the buffer is displayed. If multiple + * buffers are set as part of the same transaction, the presentation fences of all of them + * must signal before any buffer is displayed. That is, the entire transaction is delayed + * until all presentation fences have signaled, ensuring the transaction remains consistent. + * + * A releaseCallback may be passed to know when the buffer is safe to be reused. This + * is recommended when attempting to render continuously using SurfaceControl transactions + * instead of through {@link Surface}, as it provides a safe & reliable way to know when + * a buffer can be re-used. The callback will be invoked with a {@link SyncFence} which, + * if {@link SyncFence#isValid() valid}, must be waited on prior to using the buffer. This + * can either be done directly with {@link SyncFence#awaitForever()} or it may be done + * indirectly such as passing it as a release fence to + * {@link android.media.Image#setFence(SyncFence)} when using + * {@link android.media.ImageReader}. + * + * @param sc The SurfaceControl to update + * @param buffer The buffer to be displayed + * @param fence The presentation fence. If null or invalid, this is equivalent to + * {@link #setBuffer(SurfaceControl, HardwareBuffer)} + * @param releaseCallback The callback to invoke when the buffer being set has been released + * by a later transaction. That is, the point at which it is safe + * to re-use the buffer. + * @return this + */ + public @NonNull Transaction setBuffer(@NonNull SurfaceControl sc, + @Nullable HardwareBuffer buffer, @Nullable SyncFence fence, + @Nullable Consumer releaseCallback) { checkPreconditions(sc); if (fence != null) { synchronized (fence.getLock()) { nativeSetBuffer(mNativeObject, sc.mNativeObject, buffer, - fence.getNativeFence()); + fence.getNativeFence(), releaseCallback); } } else { - nativeSetBuffer(mNativeObject, sc.mNativeObject, buffer, 0); + nativeSetBuffer(mNativeObject, sc.mNativeObject, buffer, 0, releaseCallback); } return this; } + /** * Sets the buffer transform that should be applied to the current buffer. * @@ -4094,4 +4139,10 @@ public final class SurfaceControl implements Parcelable { return -1; } + + // Called by native + private static void invokeReleaseCallback(Consumer callback, long nativeFencePtr) { + SyncFence fence = new SyncFence(nativeFencePtr); + callback.accept(fence); + } } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 68025a8ba0e47..39f192b662eed 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -253,6 +253,11 @@ static struct { jfieldID alphaInterpretation; } gDisplayDecorationSupportInfo; +static struct { + jclass clazz; + jmethodID invokeReleaseCallback; +} gInvokeReleaseCallback; + class JNamedColorSpace { public: // ColorSpace.Named.SRGB.ordinal() = 0; @@ -625,8 +630,59 @@ static void nativeSetGeometry(JNIEnv* env, jclass clazz, jlong transactionObj, j transaction->setGeometry(ctrl, source, dst, orientation); } +class JGlobalRefHolder { +public: + JGlobalRefHolder(JavaVM* vm, jobject object) : mVm(vm), mObject(object) {} + + virtual ~JGlobalRefHolder() { + env()->DeleteGlobalRef(mObject); + mObject = nullptr; + } + + jobject object() { return mObject; } + JavaVM* vm() { return mVm; } + + JNIEnv* env() { + JNIEnv* env = nullptr; + if (mVm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + if (mVm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!"); + } + } + return env; + } + +private: + JGlobalRefHolder(const JGlobalRefHolder&) = delete; + void operator=(const JGlobalRefHolder&) = delete; + + JavaVM* mVm; + jobject mObject; +}; + +static ReleaseBufferCallback genReleaseCallback(JNIEnv* env, jobject releaseCallback) { + if (releaseCallback == nullptr) return nullptr; + + JavaVM* vm = nullptr; + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM"); + auto globalCallbackRef = + std::make_shared(vm, env->NewGlobalRef(releaseCallback)); + return [globalCallbackRef](const ReleaseCallbackId&, const sp& releaseFence, + std::optional currentMaxAcquiredBufferCount) { + Fence* fenceCopy = releaseFence.get(); + // We need to grab an extra ref as Java's SyncFence takes ownership + if (fenceCopy) { + fenceCopy->incStrong(0); + } + globalCallbackRef->env()->CallStaticVoidMethod(gInvokeReleaseCallback.clazz, + gInvokeReleaseCallback.invokeReleaseCallback, + globalCallbackRef->object(), + reinterpret_cast(fenceCopy)); + }; +} + static void nativeSetBuffer(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject, - jobject bufferObject, jlong fencePtr) { + jobject bufferObject, jlong fencePtr, jobject releaseCallback) { auto transaction = reinterpret_cast(transactionObj); SurfaceControl* const ctrl = reinterpret_cast(nativeObject); sp graphicBuffer(GraphicBuffer::fromAHardwareBuffer( @@ -635,7 +691,8 @@ static void nativeSetBuffer(JNIEnv* env, jclass clazz, jlong transactionObj, jlo if (fencePtr != 0) { optFence = sp{reinterpret_cast(fencePtr)}; } - transaction->setBuffer(ctrl, graphicBuffer, optFence); + transaction->setBuffer(ctrl, graphicBuffer, optFence, std::nullopt, + genReleaseCallback(env, releaseCallback)); } static void nativeSetBufferTransform(JNIEnv* env, jclass clazz, jlong transactionObj, @@ -2155,7 +2212,7 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeGetDisplayedContentSample }, {"nativeSetGeometry", "(JJLandroid/graphics/Rect;Landroid/graphics/Rect;J)V", (void*)nativeSetGeometry }, - {"nativeSetBuffer", "(JJLandroid/hardware/HardwareBuffer;J)V", + {"nativeSetBuffer", "(JJLandroid/hardware/HardwareBuffer;JLjava/util/function/Consumer;)V", (void*)nativeSetBuffer }, {"nativeSetBufferTransform", "(JJI)V", (void*) nativeSetBufferTransform}, {"nativeSetDataSpace", "(JJI)V", @@ -2451,6 +2508,12 @@ int register_android_view_SurfaceControl(JNIEnv* env) gDisplayDecorationSupportInfo.alphaInterpretation = GetFieldIDOrDie(env, displayDecorationSupportClazz, "alphaInterpretation", "I"); + jclass surfaceControlClazz = FindClassOrDie(env, "android/view/SurfaceControl"); + gInvokeReleaseCallback.clazz = MakeGlobalRefOrDie(env, surfaceControlClazz); + gInvokeReleaseCallback.invokeReleaseCallback = + GetStaticMethodIDOrDie(env, surfaceControlClazz, "invokeReleaseCallback", + "(Ljava/util/function/Consumer;J)V"); + return err; }