From 5e09609eda2919ddd40d1b6175518cbe0a3d3936 Mon Sep 17 00:00:00 2001 From: Rachel Lee Date: Tue, 8 Oct 2024 13:09:44 -0700 Subject: Add new Surface#setFrameRate API The new overload uses same plumbing and same logic as the other setFrameRate overloads. This plumbing and logic will be upgraded to accommodate new parameters in a future CL (e.g. for desiredMaxRate). Bug: 362798998 Test: atest SetFrameRateTest Flag: com.android.graphics.surfaceflinger.flags.arr_setframerate_api Change-Id: I45b1dab08f28d1891fd8659ee0b0b194efd5b1ff --- core/api/current.txt | 17 +++ core/java/android/view/Surface.java | 207 ++++++++++++++++++++++++++++++++++++ 2 files changed, 224 insertions(+) diff --git a/core/api/current.txt b/core/api/current.txt index 53da33835d31..ed067ad1b6a8 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -52380,6 +52380,7 @@ package android.view { method public android.graphics.Canvas lockHardwareCanvas(); method public void readFromParcel(android.os.Parcel); method public void release(); + method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public void setFrameRate(@NonNull android.view.Surface.FrameRateParams); method public void setFrameRate(@FloatRange(from=0.0) float, int, int); method public void setFrameRate(@FloatRange(from=0.0) float, int); method @Deprecated public void unlockCanvas(android.graphics.Canvas); @@ -52396,6 +52397,22 @@ package android.view { field public static final int ROTATION_90 = 1; // 0x1 } + @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public static class Surface.FrameRateParams { + method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public int getChangeFrameRateStrategy(); + method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public float getDesiredMaxRate(); + method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public float getDesiredMinRate(); + method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public float getFixedSourceRate(); + field @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public static final android.view.Surface.FrameRateParams IGNORE; + } + + @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public static final class Surface.FrameRateParams.Builder { + ctor public Surface.FrameRateParams.Builder(); + method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") @NonNull public android.view.Surface.FrameRateParams build(); + method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") @NonNull public android.view.Surface.FrameRateParams.Builder setChangeFrameRateStrategy(int); + method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") @NonNull public android.view.Surface.FrameRateParams.Builder setDesiredRateRange(@FloatRange(from=0.0) float, @FloatRange(from=0.0) float); + method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") @NonNull public android.view.Surface.FrameRateParams.Builder setFixedSourceRate(@FloatRange(from=0.0) float); + } + public static class Surface.OutOfResourcesException extends java.lang.RuntimeException { ctor public Surface.OutOfResourcesException(); ctor public Surface.OutOfResourcesException(String); diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 396be7b62b4d..03f9d9814b43 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -18,9 +18,11 @@ package android.view; import static android.system.OsConstants.EINVAL; +import android.annotation.FlaggedApi; import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SuppressLint; import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ActivityInfo; import android.content.res.CompatibilityInfo.Translator; @@ -1025,6 +1027,211 @@ public class Surface implements Parcelable { return mIsAutoRefreshEnabled; } + /** + * Parameter object for {@link #setFrameRate(FrameRateParams)}, describing the intended frame + * rate for the Surface that setFrameRate is called on. + */ + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public static class FrameRateParams { + private FrameRateParams() {} + + /** + * A static FrameRateParams that can be passed directly into {@link + * #setFrameRate(FrameRateParams)} to indicate the surface has no preference and any frame + * rate is acceptable. + */ + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public static final FrameRateParams IGNORE = + new FrameRateParams.Builder().setDesiredRateRange(0f, Float.MAX_VALUE).build(); + + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public static final class Builder { + private float mDesiredMinRate; + private float mDesiredMaxRate; + private float mFixedSourceRate; + private int mChangeFrameRateStrategy; + + /** + * Sets the desired frame rate range (inclusive) values for the surface, specifying that + * the surface prefers the device render rate to be in the range [desiredMinRate, + * desiredMaxRate]. + + * Set desiredMaxRate to FLOAT.MAX_VALUE to indicate the surface prefers any value + * greater than or equal to desiredMinRate. + * + * Set desiredMinRate = desiredMaxRate to indicate the surface prefers an exact frame + * rate. Note that this is different than specifying the fixed source frame rate with + * {@link FrameRateParams.Builder#setFixedSourceRate}. To reiterate, this call is used + * to specify the surface's frame rate preference to be within the desired range. + * + * desiredMaxRate must be greater than or equal to desiredMinRate. + * The values should be greater than or equal to 0. + * + * If the surface has no preference and any frame rate is acceptable, use the constant + * {@link FrameRateParams.IGNORE} in {@link #setFrameRate(FrameRateParams)} instead of + * building {@link FrameRateParams.Builder}. + * + * @see FrameRateParams#getDesiredMinRate() + * @see FrameRateParams#getDesiredMaxRate() + */ + @SuppressLint("MissingGetterMatchingBuilder") + @NonNull + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public Builder setDesiredRateRange(@FloatRange(from = 0.0) float desiredMinRate, + @FloatRange(from = 0.0) float desiredMaxRate) { + if (desiredMaxRate < desiredMinRate) { + Log.e(TAG, + "Failed to set desired frame rate range. desiredMaxRate should be " + + "greater than or equal to desiredMinRate"); + return this; + } + mDesiredMinRate = desiredMinRate; + mDesiredMaxRate = desiredMaxRate; + return this; + } + + /** + * Sets the fixed frame rate of the surface when its content has a fixed frame rate, + * e.g. a video with a fixed frame rate. + * + * When the frame rate chosen for the surface is the {@code fixedSourceRate} or a + * multiple, the surface can render without frame pulldown, for optimal smoothness. For + * example, a 30 fps video ({@code fixedSourceRate=30}) renders just as well on 30 fps, + * 60 fps, 90 fps, 120 fps, and so on. + * + * This method to set the fixed source rate can also be used together with a desired + * frame rate range via {@link FrameRateParams.Builder#setDesiredRateRange}. This still + * means the surface's content has a fixed frame rate of the provided {@code + * fixedSourceRate}, as well as it preferring to be within the desired frame rate range. + * For example, a 30 fps video {@code fixedSourceRate=30} and desired frame rate range + * [60,90] means the surface ideally prefers 60 fps (which is 30 fps * 2) or 90 fps (30 + * fps * 3). + * + * @see FrameRateParams#getFixedSourceRate() + */ + @NonNull + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public Builder setFixedSourceRate(@FloatRange(from = 0.0) float fixedSourceRate) { + mFixedSourceRate = fixedSourceRate; + return this; + } + + /** + * Whether display refresh rate transitions caused by this surface should be seamless. A + * seamless transition is one that doesn't have any visual interruptions, such as a + * black screen for a second or two. Value is + * Surface.CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, or Surface.CHANGE_FRAME_RATE_ALWAYS + * + * @see FrameRateParams#getChangeFrameRateStrategy() + */ + @NonNull + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public Builder setChangeFrameRateStrategy( + @ChangeFrameRateStrategy int changeFrameRateStrategy) { + mChangeFrameRateStrategy = changeFrameRateStrategy; + return this; + } + + /** + * Builds the FrameRateParams object. + */ + @NonNull + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public FrameRateParams build() { + FrameRateParams frameRate = new FrameRateParams(); + frameRate.mDesiredMinRate = this.mDesiredMinRate; + frameRate.mDesiredMaxRate = this.mDesiredMaxRate; + frameRate.mFixedSourceRate = this.mFixedSourceRate; + frameRate.mChangeFrameRateStrategy = this.mChangeFrameRateStrategy; + return frameRate; + } + } + + /** + * Gets the minimum desired frame rate. + * @see FrameRateParams.Builder#setDesiredRateRange() + */ + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public float getDesiredMinRate() { + return mDesiredMinRate; + } + + /** + * Gets the maximum desired frame rate. + * @see FrameRateParams.Builder#setDesiredRateRange() + */ + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public float getDesiredMaxRate() { + return mDesiredMaxRate; + } + + /** + * Gets the fixed source frame rate. + * @see FrameRateParams.Builder#setFixedSourceRate() + */ + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public float getFixedSourceRate() { + return mFixedSourceRate; + } + + /** + * Gets the strategy when changing frame rate. + * @see FrameRateParams.Builder#setChangeFrameRateStrategy + */ + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + @ChangeFrameRateStrategy + public int getChangeFrameRateStrategy() { + return mChangeFrameRateStrategy; + } + + float mDesiredMinRate; + float mDesiredMaxRate; + float mFixedSourceRate; + int mChangeFrameRateStrategy; + } + + /** + * Sets the intended frame rate for this surface. + * + *

On devices that are capable of running the display at different frame 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 frameRateParams The parameters describing the intended frame rate of this surface. + * Refer to {@link FrameRateParams} for details. + * @throws IllegalArgumentException If frameRateParams is invalid. + * @see #clearFrameRate() + */ + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public void setFrameRate(@NonNull FrameRateParams frameRateParams) { + synchronized (mLock) { + checkNotReleasedLocked(); + // TODO(b/362798998): Logic currently incomplete: it uses fixed source rate over the + // desired min/max rates. Fix when plumbing is upgraded. + int compatibility = frameRateParams.getFixedSourceRate() == 0 + ? FRAME_RATE_COMPATIBILITY_DEFAULT + : FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; + float frameRate = compatibility == FRAME_RATE_COMPATIBILITY_DEFAULT + ? frameRateParams.getDesiredMinRate() + : frameRateParams.getFixedSourceRate(); + int error = nativeSetFrameRate(mNativeObject, frameRate, compatibility, + frameRateParams.getChangeFrameRateStrategy()); + if (error == -EINVAL) { + throw new IllegalArgumentException("Invalid argument to Surface.setFrameRate()"); + } else if (error != 0) { + Log.e(TAG, "Failed to set frame rate on Surface. Native error: " + error); + } + } + } + /** * Sets the intended frame rate for this surface. * -- cgit v1.2.3-59-g8ed1b