diff --git a/api/current.txt b/api/current.txt index c4ac8336f3304..c78f090edf284 100644 --- a/api/current.txt +++ b/api/current.txt @@ -52446,6 +52446,7 @@ package android.view { method public android.graphics.Canvas lockHardwareCanvas(); method public void readFromParcel(android.os.Parcel); method public void release(); + method public void setFrameRate(@FloatRange(from=0.0) float); method @Deprecated public void unlockCanvas(android.graphics.Canvas); method public void unlockCanvasAndPost(android.graphics.Canvas); method public void writeToParcel(android.os.Parcel, int); @@ -52489,6 +52490,7 @@ package android.view { method @NonNull public android.view.SurfaceControl.Transaction reparent(@NonNull android.view.SurfaceControl, @Nullable android.view.SurfaceControl); 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 setBufferSize(@NonNull android.view.SurfaceControl, @IntRange(from=0) int, @IntRange(from=0) int); + method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float); method @NonNull public android.view.SurfaceControl.Transaction setGeometry(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, int); method @NonNull public android.view.SurfaceControl.Transaction setLayer(@NonNull android.view.SurfaceControl, @IntRange(from=java.lang.Integer.MIN_VALUE, to=java.lang.Integer.MAX_VALUE) int); method @NonNull public android.view.SurfaceControl.Transaction setVisibility(@NonNull android.view.SurfaceControl, boolean); diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 7707ad163b85f..a6b7c33de3d9c 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -16,6 +16,7 @@ package android.view; +import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; @@ -88,6 +89,8 @@ public class Surface implements Parcelable { private static native int nativeSetSharedBufferModeEnabled(long nativeObject, boolean enabled); private static native int nativeSetAutoRefreshEnabled(long nativeObject, boolean enabled); + private static native int nativeSetFrameRate(long nativeObject, float frameRate); + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override @@ -840,6 +843,34 @@ public class Surface implements Parcelable { return mIsAutoRefreshEnabled; } + /** + * Sets the intended frame rate for this surface. + * + * On devices that are capable of running the display at different refresh rates, the + * system may choose a display refresh rate to better match this surface's frame + * rate. Usage of this API won't introduce frame rate throttling, or affect other + * aspects of the application's frame production pipeline. However, because the system + * may change the display refresh rate, calls to this function may result in changes + * to Choreographer callback timings, and changes to the time interval at which the + * system releases buffers back to the application. + * + * Note that this only has an effect for surfaces presented on the display. If this + * surface is consumed by something other than the system compositor, e.g. a media + * codec, this call has no effect. + * + * @param frameRate The intended frame rate of this surface. 0 is a special value that + * indicates the app will accept the system's choice for the display frame rate, which + * is the default behavior if this function isn't called. The frameRate param does + * *not* need to be a valid refresh rate for this device's display - e.g., it's fine + * to pass 30fps to a device that can only run the display at 60fps. + */ + public void setFrameRate(@FloatRange(from = 0.0) float frameRate) { + int error = nativeSetFrameRate(mNativeObject, frameRate); + if (error != 0) { + throw new RuntimeException("Failed to set frame rate on Surface"); + } + } + /** * Exception thrown when a Canvas couldn't be locked with {@link Surface#lockCanvas}, or * when a SurfaceTexture could not successfully be allocated. diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index bcc9e41b8ab07..f7b87cce73381 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -211,6 +211,9 @@ public final class SurfaceControl implements Parcelable { private static native void nativeSetGlobalShadowSettings(@Size(4) float[] ambientColor, @Size(4) float[] spotColor, float lightPosY, float lightPosZ, float lightRadius); + private static native void nativeSetFrameRate( + long transactionObj, long nativeObject, float frameRate); + private final CloseGuard mCloseGuard = CloseGuard.get(); private String mName; /** @@ -2786,6 +2789,33 @@ public final class SurfaceControl implements Parcelable { return this; } + /** + * Sets the intended frame rate for the surface {@link SurfaceControl}. + * + * On devices that are capable of running the display at different refresh rates, the system + * may choose a display refresh rate to better match this surface's frame rate. Usage of + * this API won't directly affect the application's frame production pipeline. However, + * because the system may change the display refresh rate, calls to this function may result + * in changes to Choreographer callback timings, and changes to the time interval at which + * the system releases buffers back to the application. + * + * @param sc The SurfaceControl to specify the frame rate of. + * @param frameRate The intended frame rate for this surface. 0 is a special value that + * indicates the app will accept the system's choice for the display frame + * rate, which is the default behavior if this function isn't called. The + * frameRate param does *not* need to be a valid refresh rate for this + * device's display - e.g., it's fine to pass 30fps to a device that can + * only run the display at 60fps. + * @return This transaction object. + */ + @NonNull + public Transaction setFrameRate( + @NonNull SurfaceControl sc, @FloatRange(from = 0.0) float frameRate) { + checkPreconditions(sc); + nativeSetFrameRate(mNativeObject, sc.mNativeObject, frameRate); + return this; + } + /** * Merge the other transaction into this transaction, clearing the * other transaction as if it had been applied. diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index 01b5920755fa4..b01083bba643b 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -413,6 +413,12 @@ static jint nativeSetAutoRefreshEnabled(JNIEnv* env, jclass clazz, jlong nativeO return anw->perform(surface, NATIVE_WINDOW_SET_AUTO_REFRESH, int(enabled)); } +static jint nativeSetFrameRate(JNIEnv* env, jclass clazz, jlong nativeObject, jfloat frameRate) { + Surface* surface = reinterpret_cast(nativeObject); + ANativeWindow* anw = static_cast(surface); + return anw->perform(surface, NATIVE_WINDOW_SET_FRAME_RATE, float(frameRate)); +} + // ---------------------------------------------------------------------------- static const JNINativeMethod gSurfaceMethods[] = { @@ -447,6 +453,7 @@ static const JNINativeMethod gSurfaceMethods[] = { (void*)nativeAttachAndQueueBufferWithColorSpace}, {"nativeSetSharedBufferModeEnabled", "(JZ)I", (void*)nativeSetSharedBufferModeEnabled}, {"nativeSetAutoRefreshEnabled", "(JZ)I", (void*)nativeSetAutoRefreshEnabled}, + {"nativeSetFrameRate", "(JF)I", (void*)nativeSetFrameRate}, }; int register_android_view_Surface(JNIEnv* env) diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index e0f9571472dad..2b9d454315827 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -584,6 +584,14 @@ static void nativeSetShadowRadius(JNIEnv* env, jclass clazz, jlong transactionOb transaction->setShadowRadius(ctrl, shadowRadius); } +static void nativeSetFrameRate(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject, + jfloat frameRate) { + auto transaction = reinterpret_cast(transactionObj); + + const auto ctrl = reinterpret_cast(nativeObject); + transaction->setFrameRate(ctrl, frameRate); +} + static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) { const auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds(); jlongArray array = env->NewLongArray(displayIds.size()); @@ -1383,6 +1391,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetLayerStack }, {"nativeSetShadowRadius", "(JJF)V", (void*)nativeSetShadowRadius }, + {"nativeSetFrameRate", "(JJF)V", + (void*)nativeSetFrameRate }, {"nativeGetPhysicalDisplayIds", "()[J", (void*)nativeGetPhysicalDisplayIds }, {"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;", diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index 203adfc749d24..97b861b390ad2 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -240,6 +240,7 @@ LIBANDROID { ASurfaceTransaction_setColor; # introduced=29 ASurfaceTransaction_setDamageRegion; # introduced=29 ASurfaceTransaction_setDesiredPresentTime; # introduced=29 + ASurfaceTransaction_setFrameRate; # introduced=30 ASurfaceTransaction_setGeometry; # introduced=29 ASurfaceTransaction_setHdrMetadata_cta861_3; # introduced=29 ASurfaceTransaction_setHdrMetadata_smpte2086; # introduced=29 diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp index b34b31ac84392..392c9f6404bab 100644 --- a/native/android/surface_control.cpp +++ b/native/android/surface_control.cpp @@ -545,3 +545,18 @@ void ASurfaceTransaction_setColor(ASurfaceTransaction* aSurfaceTransaction, transaction->setBackgroundColor(surfaceControl, color, alpha, static_cast(dataspace)); } + +void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* aSurfaceTransaction, + ASurfaceControl* aSurfaceControl, float frameRate) { + CHECK_NOT_NULL(aSurfaceTransaction); + CHECK_NOT_NULL(aSurfaceControl); + + sp surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl); + if (frameRate < 0) { + ALOGE("Failed to set frame ate - invalid frame rate"); + return; + } + + Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction); + transaction->setFrameRate(surfaceControl, frameRate); +}