diff options
12 files changed, 251 insertions, 3 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 8bd4367dece8..bebf98a29424 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -51080,6 +51080,7 @@ package android.view { method @NonNull public android.view.DisplayShape getShape(); method @Deprecated public void getSize(android.graphics.Point); method public int getState(); + method @FlaggedApi("com.android.server.display.feature.flags.enable_get_suggested_frame_rate") public float getSuggestedFrameRate(int); method public android.view.Display.Mode[] getSupportedModes(); method @Deprecated public float[] getSupportedRefreshRates(); method @Deprecated public int getWidth(); @@ -51097,6 +51098,8 @@ package android.view { field public static final int FLAG_ROUND = 16; // 0x10 field public static final int FLAG_SECURE = 2; // 0x2 field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1 + field @FlaggedApi("com.android.server.display.feature.flags.enable_get_suggested_frame_rate") public static final int FRAME_RATE_CATEGORY_HIGH = 1; // 0x1 + field @FlaggedApi("com.android.server.display.feature.flags.enable_get_suggested_frame_rate") public static final int FRAME_RATE_CATEGORY_NORMAL = 0; // 0x0 field public static final int INVALID_DISPLAY = -1; // 0xffffffff field public static final int STATE_DOZE = 3; // 0x3 field public static final int STATE_DOZE_SUSPEND = 4; // 0x4 diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index e940e55bd38b..910e644f7b6f 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -22,6 +22,7 @@ import static android.hardware.flags.Flags.FLAG_OVERLAYPROPERTIES_CLASS_API; import static com.android.server.display.feature.flags.Flags.FLAG_HIGHEST_HDR_SDR_RATIO_API; import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_HAS_ARR_SUPPORT; +import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE; import android.Manifest; import android.annotation.FlaggedApi; @@ -1278,6 +1279,60 @@ public final class Display { } } + @FlaggedApi(FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE) + public static final int FRAME_RATE_CATEGORY_NORMAL = 0; + @FlaggedApi(FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE) + public static final int FRAME_RATE_CATEGORY_HIGH = 1; + + /** + * @hide + */ + @IntDef(prefix = {"FRAME_RATE_CATEGORY_"}, + value = { + FRAME_RATE_CATEGORY_NORMAL, + FRAME_RATE_CATEGORY_HIGH + }) + @Retention(RetentionPolicy.SOURCE) + public @interface FrameRateCategory {} + + /** + * <p> Gets the display-defined frame rate given a descriptive frame rate category. </p> + * + * <p> For example, an animation that does not require fast render rates can use + * the {@link #FRAME_RATE_CATEGORY_NORMAL} to get the suggested frame rate. + * The suggested frame rate then can be used in the + * {@link Surface.FrameRateParams.Builder#setDesiredRateRange} for desiredMinRate. + * + * <pre>{@code + * float desiredMinRate = display.getSuggestedFrameRate(FRAME_RATE_CATEGORY_NORMAL); + * Surface.FrameRateParams params = new Surface.FrameRateParams.Builder(). + * setDesiredRateRange(desiredMinRate, Float.MAX).build(); + * surface.setFrameRate(params); + * }</pre> + * </p> + * + * @param category either {@link #FRAME_RATE_CATEGORY_NORMAL} + * or {@link #FRAME_RATE_CATEGORY_HIGH} + * + * @see Surface#setFrameRate(Surface.FrameRateParams) + * @see SurfaceControl.Transaction#setFrameRate(SurfaceControl, Surface.FrameRateParams) + * @throws IllegalArgumentException when category is not {@link #FRAME_RATE_CATEGORY_NORMAL} + * or {@link #FRAME_RATE_CATEGORY_HIGH} + */ + @FlaggedApi(FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE) + public float getSuggestedFrameRate(@FrameRateCategory int category) { + synchronized (mLock) { + updateDisplayInfoLocked(); + if (category == FRAME_RATE_CATEGORY_HIGH) { + return mDisplayInfo.frameRateCategoryRate.getHigh(); + } else if (category == FRAME_RATE_CATEGORY_NORMAL) { + return mDisplayInfo.frameRateCategoryRate.getNormal(); + } else { + throw new IllegalArgumentException("Invalid FrameRateCategory provided"); + } + } + } + /** * <p> Returns true if the connected display can be switched into a mode with minimal * post processing. </p> diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index 26fce904eb5e..8f112f338a00 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -204,6 +204,12 @@ public final class DisplayInfo implements Parcelable { public boolean hasArrSupport; /** + * Represents frame rate for the FrameRateCategory Normal and High. + * @see android.view.Display#getSuggestedFrameRate(int) for more details. + */ + public FrameRateCategoryRate frameRateCategoryRate; + + /** * The default display mode. */ public int defaultModeId; @@ -443,6 +449,7 @@ public final class DisplayInfo implements Parcelable { && modeId == other.modeId && renderFrameRate == other.renderFrameRate && hasArrSupport == other.hasArrSupport + && Objects.equals(frameRateCategoryRate, other.frameRateCategoryRate) && defaultModeId == other.defaultModeId && userPreferredModeId == other.userPreferredModeId && Arrays.equals(supportedModes, other.supportedModes) @@ -505,6 +512,7 @@ public final class DisplayInfo implements Parcelable { modeId = other.modeId; renderFrameRate = other.renderFrameRate; hasArrSupport = other.hasArrSupport; + frameRateCategoryRate = other.frameRateCategoryRate; defaultModeId = other.defaultModeId; userPreferredModeId = other.userPreferredModeId; supportedModes = Arrays.copyOf(other.supportedModes, other.supportedModes.length); @@ -562,6 +570,8 @@ public final class DisplayInfo implements Parcelable { modeId = source.readInt(); renderFrameRate = source.readFloat(); hasArrSupport = source.readBoolean(); + frameRateCategoryRate = source.readParcelable(null, + android.view.FrameRateCategoryRate.class); defaultModeId = source.readInt(); userPreferredModeId = source.readInt(); int nModes = source.readInt(); @@ -636,6 +646,7 @@ public final class DisplayInfo implements Parcelable { dest.writeInt(modeId); dest.writeFloat(renderFrameRate); dest.writeBoolean(hasArrSupport); + dest.writeParcelable(frameRateCategoryRate, flags); dest.writeInt(defaultModeId); dest.writeInt(userPreferredModeId); dest.writeInt(supportedModes.length); @@ -883,6 +894,8 @@ public final class DisplayInfo implements Parcelable { sb.append(renderFrameRate); sb.append(", hasArrSupport "); sb.append(hasArrSupport); + sb.append(", frameRateCategoryRate "); + sb.append(frameRateCategoryRate); sb.append(", defaultMode "); sb.append(defaultModeId); sb.append(", userPreferredModeId "); diff --git a/core/java/android/view/FrameRateCategoryRate.java b/core/java/android/view/FrameRateCategoryRate.java new file mode 100644 index 000000000000..3c674b8966d1 --- /dev/null +++ b/core/java/android/view/FrameRateCategoryRate.java @@ -0,0 +1,113 @@ +/* + * Copyright 2024 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. + */ + +package android.view; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A class to create and manage FrameRateCategoryRate for + * categories Normal and High. + * @hide + */ +public class FrameRateCategoryRate implements Parcelable { + + private final float mNormal; + private final float mHigh; + + /** + * Creates a FrameRateCategoryRate with the provided rates + * for the categories Normal and High respectively; + * + * @param normal rate for the category Normal. + * @param high rate for the category High. + * @hide + */ + public FrameRateCategoryRate(float normal, float high) { + this.mNormal = normal; + this.mHigh = high; + } + + /** + * @return the value for the category normal; + * @hide + */ + public float getNormal() { + return mNormal; + } + + /** + * @return the value for the category high; + * @hide + */ + public float getHigh() { + return mHigh; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof FrameRateCategoryRate)) { + return false; + } + FrameRateCategoryRate that = (FrameRateCategoryRate) o; + return mNormal == that.mNormal && mHigh == that.mHigh; + + } + + @Override + public int hashCode() { + int result = 1; + result = 31 * result + Float.hashCode(mNormal); + result = 31 * result + Float.hashCode(mHigh); + return result; + } + + @Override + public String toString() { + return "FrameRateCategoryRate {" + + "normal=" + mNormal + + ", high=" + mHigh + + '}'; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeFloat(mNormal); + dest.writeFloat(mHigh); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator<FrameRateCategoryRate> CREATOR = + new Creator<>() { + @Override + public FrameRateCategoryRate createFromParcel(Parcel in) { + return new FrameRateCategoryRate(in.readFloat(), in.readFloat()); + } + + @Override + public FrameRateCategoryRate[] newArray(int size) { + return new FrameRateCategoryRate[size]; + } + }; +} diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index ece2a6068934..0d5554484dc1 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -1798,6 +1798,7 @@ public final class SurfaceControl implements Parcelable { public int activeDisplayModeId; public float renderFrameRate; public boolean hasArrSupport; + public FrameRateCategoryRate frameRateCategoryRate; public int[] supportedColorModes; public int activeColorMode; @@ -1816,6 +1817,7 @@ public final class SurfaceControl implements Parcelable { + ", activeDisplayModeId=" + activeDisplayModeId + ", renderFrameRate=" + renderFrameRate + ", hasArrSupport=" + hasArrSupport + + ", frameRateCategoryRate=" + frameRateCategoryRate + ", supportedColorModes=" + Arrays.toString(supportedColorModes) + ", activeColorMode=" + activeColorMode + ", hdrCapabilities=" + hdrCapabilities @@ -1836,13 +1838,15 @@ public final class SurfaceControl implements Parcelable { && activeColorMode == that.activeColorMode && Objects.equals(hdrCapabilities, that.hdrCapabilities) && preferredBootDisplayMode == that.preferredBootDisplayMode - && hasArrSupport == that.hasArrSupport; + && hasArrSupport == that.hasArrSupport + && Objects.equals(frameRateCategoryRate, that.frameRateCategoryRate); } @Override public int hashCode() { return Objects.hash(Arrays.hashCode(supportedDisplayModes), activeDisplayModeId, - renderFrameRate, activeColorMode, hdrCapabilities, hasArrSupport); + renderFrameRate, activeColorMode, hdrCapabilities, hasArrSupport, + frameRateCategoryRate); } } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index f162b7410b10..7fefe1755fa9 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -117,6 +117,7 @@ static struct { jfieldID activeDisplayModeId; jfieldID renderFrameRate; jfieldID hasArrSupport; + jfieldID frameRateCategoryRate; jfieldID supportedColorModes; jfieldID activeColorMode; jfieldID hdrCapabilities; @@ -292,6 +293,11 @@ static struct { jfieldID frameNumber; } gStalledTransactionInfoClassInfo; +static struct { + jclass clazz; + jmethodID ctor; +} gFrameRateCategoryRateClassInfo; + constexpr ui::Dataspace pickDataspaceFromColorMode(const ui::ColorMode colorMode) { switch (colorMode) { case ui::ColorMode::DISPLAY_P3: @@ -1388,6 +1394,13 @@ static jobject nativeGetStaticDisplayInfo(JNIEnv* env, jclass clazz, jlong id) { return object; } +static jobject convertFrameRateCategoryRateToJavaObject( + JNIEnv* env, const ui::FrameRateCategoryRate& frameRateCategoryRate) { + return env->NewObject(gFrameRateCategoryRateClassInfo.clazz, + gFrameRateCategoryRateClassInfo.ctor, frameRateCategoryRate.getNormal(), + frameRateCategoryRate.getHigh()); +} + static jobject convertDisplayModeToJavaObject(JNIEnv* env, const ui::DisplayMode& config) { jobject object = env->NewObject(gDisplayModeClassInfo.clazz, gDisplayModeClassInfo.ctor); env->SetIntField(object, gDisplayModeClassInfo.id, config.id); @@ -1456,6 +1469,8 @@ static jobject nativeGetDynamicDisplayInfo(JNIEnv* env, jclass clazz, jlong disp info.activeDisplayModeId); env->SetFloatField(object, gDynamicDisplayInfoClassInfo.renderFrameRate, info.renderFrameRate); env->SetBooleanField(object, gDynamicDisplayInfoClassInfo.hasArrSupport, info.hasArrSupport); + env->SetObjectField(object, gDynamicDisplayInfoClassInfo.frameRateCategoryRate, + convertFrameRateCategoryRateToJavaObject(env, info.frameRateCategoryRate)); jintArray colorModesArray = env->NewIntArray(info.supportedColorModes.size()); if (colorModesArray == NULL) { jniThrowException(env, "java/lang/OutOfMemoryError", NULL); @@ -2666,6 +2681,15 @@ int register_android_view_SurfaceControl(JNIEnv* env) GetFieldIDOrDie(env, dynamicInfoClazz, "renderFrameRate", "F"); gDynamicDisplayInfoClassInfo.hasArrSupport = GetFieldIDOrDie(env, dynamicInfoClazz, "hasArrSupport", "Z"); + + gDynamicDisplayInfoClassInfo.frameRateCategoryRate = + GetFieldIDOrDie(env, dynamicInfoClazz, "frameRateCategoryRate", + "Landroid/view/FrameRateCategoryRate;"); + jclass frameRateCategoryRateClazz = FindClassOrDie(env, "android/view/FrameRateCategoryRate"); + gFrameRateCategoryRateClassInfo.clazz = MakeGlobalRefOrDie(env, frameRateCategoryRateClazz); + gFrameRateCategoryRateClassInfo.ctor = + GetMethodIDOrDie(env, frameRateCategoryRateClazz, "<init>", "(FF)V"); + gDynamicDisplayInfoClassInfo.supportedColorModes = GetFieldIDOrDie(env, dynamicInfoClazz, "supportedColorModes", "[I"); gDynamicDisplayInfoClassInfo.activeColorMode = diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java index 0807c70d9922..4ad7c10a1444 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java @@ -26,6 +26,7 @@ import android.view.DisplayAddress; import android.view.DisplayCutout; import android.view.DisplayEventReceiver; import android.view.DisplayShape; +import android.view.FrameRateCategoryRate; import android.view.RoundedCorners; import android.view.Surface; @@ -300,6 +301,11 @@ final class DisplayDeviceInfo { public boolean hasArrSupport; /** + * Represents frame rate for the FrameRateCategory Normal and High. + * @see android.view.Display#getSuggestedFrameRate(int) for more details. + */ + public FrameRateCategoryRate frameRateCategoryRate; + /** * The default mode of the display. */ public int defaultModeId; @@ -548,7 +554,8 @@ final class DisplayDeviceInfo { || !Objects.equals(roundedCorners, other.roundedCorners) || installOrientation != other.installOrientation || !Objects.equals(displayShape, other.displayShape) - || hasArrSupport != other.hasArrSupport) { + || hasArrSupport != other.hasArrSupport + || !Objects.equals(frameRateCategoryRate, other.frameRateCategoryRate)) { diff |= DIFF_OTHER; } return diff; @@ -567,6 +574,7 @@ final class DisplayDeviceInfo { modeId = other.modeId; renderFrameRate = other.renderFrameRate; hasArrSupport = other.hasArrSupport; + frameRateCategoryRate = other.frameRateCategoryRate; defaultModeId = other.defaultModeId; userPreferredModeId = other.userPreferredModeId; supportedModes = other.supportedModes; @@ -612,6 +620,7 @@ final class DisplayDeviceInfo { sb.append(", modeId ").append(modeId); sb.append(", renderFrameRate ").append(renderFrameRate); sb.append(", hasArrSupport ").append(hasArrSupport); + sb.append(", frameRateCategoryRate ").append(frameRateCategoryRate); sb.append(", defaultModeId ").append(defaultModeId); sb.append(", userPreferredModeId ").append(userPreferredModeId); sb.append(", supportedModes ").append(Arrays.toString(supportedModes)); diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index f9c3a46828b9..a4bb8c348d3f 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -45,6 +45,7 @@ import android.view.DisplayAddress; import android.view.DisplayCutout; import android.view.DisplayEventReceiver; import android.view.DisplayShape; +import android.view.FrameRateCategoryRate; import android.view.RoundedCorners; import android.view.SurfaceControl; @@ -247,6 +248,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { private boolean mDisplayModeSpecsInvalid; private int mActiveColorMode; private boolean mHasArrSupport; + private FrameRateCategoryRate mFrameRateCategoryRate; private Display.HdrCapabilities mHdrCapabilities; private boolean mAllmSupported; private boolean mGameContentTypeSupported; @@ -313,6 +315,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { changed |= updateAllmSupport(dynamicInfo.autoLowLatencyModeSupported); changed |= updateGameContentTypeSupport(dynamicInfo.gameContentTypeSupported); changed |= updateHasArrSupportLocked(dynamicInfo.hasArrSupport); + changed |= updateFrameRateCategoryRatesLocked(dynamicInfo.frameRateCategoryRate); if (changed) { mHavePendingChanges = true; @@ -604,6 +607,15 @@ final class LocalDisplayAdapter extends DisplayAdapter { return true; } + private boolean updateFrameRateCategoryRatesLocked( + FrameRateCategoryRate newFrameRateCategoryRate) { + if (Objects.equals(mFrameRateCategoryRate, newFrameRateCategoryRate)) { + return false; + } + mFrameRateCategoryRate = newFrameRateCategoryRate; + return true; + } + private boolean updateHasArrSupportLocked(boolean newHasArrSupport) { if (mHasArrSupport == newHasArrSupport) { return false; @@ -695,6 +707,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { } mInfo.hdrCapabilities = mHdrCapabilities; mInfo.hasArrSupport = mHasArrSupport; + mInfo.frameRateCategoryRate = mFrameRateCategoryRate; mInfo.appVsyncOffsetNanos = mActiveSfDisplayMode.appVsyncOffsetNanos; mInfo.presentationDeadlineNanos = mActiveSfDisplayMode.presentationDeadlineNanos; mInfo.state = mState; diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 074a4d851aef..7cfdcafcb610 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -507,6 +507,7 @@ final class LogicalDisplay { mBaseDisplayInfo.modeId = deviceInfo.modeId; mBaseDisplayInfo.renderFrameRate = deviceInfo.renderFrameRate; mBaseDisplayInfo.hasArrSupport = deviceInfo.hasArrSupport; + mBaseDisplayInfo.frameRateCategoryRate = deviceInfo.frameRateCategoryRate; mBaseDisplayInfo.defaultModeId = deviceInfo.defaultModeId; mBaseDisplayInfo.userPreferredModeId = deviceInfo.userPreferredModeId; mBaseDisplayInfo.supportedModes = Arrays.copyOf( diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig index 36cadf5271c4..f49608b742b2 100644 --- a/services/core/java/com/android/server/display/feature/display_flags.aconfig +++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig @@ -430,3 +430,11 @@ flag { bug: "350617205" is_fixed_read_only: true } + +flag { + name: "enable_get_suggested_frame_rate" + namespace: "core_graphics" + description: "Flag for an API to get suggested frame rates" + bug: "361433796" + is_fixed_read_only: true +} diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java index cbe3d79f10fc..6f8c17a9ac75 100644 --- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java +++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java @@ -402,6 +402,7 @@ class DeferredDisplayUpdater { || first.modeId != second.modeId || first.renderFrameRate != second.renderFrameRate || first.hasArrSupport != second.hasArrSupport + || !Objects.equals(first.frameRateCategoryRate, second.frameRateCategoryRate) || first.defaultModeId != second.defaultModeId || first.userPreferredModeId != second.userPreferredModeId || !Arrays.equals(first.supportedModes, second.supportedModes) diff --git a/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java b/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java index 44b69f18eb04..c9c31dfe5307 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java @@ -39,6 +39,7 @@ import android.view.DisplayAddress; import android.view.DisplayCutout; import android.view.DisplayInfo; import android.view.DisplayShape; +import android.view.FrameRateCategoryRate; import android.view.RoundedCorner; import android.view.RoundedCorners; import android.view.SurfaceControl.RefreshRateRange; @@ -235,6 +236,9 @@ public class DeferredDisplayUpdaterDiffTest { } else if (type.isArray() && type.getComponentType().equals(Display.Mode.class)) { field.set(first, new Display.Mode[]{new Display.Mode(100, 200, 300)}); field.set(second, new Display.Mode[]{new Display.Mode(10, 20, 30)}); + } else if (type.equals(FrameRateCategoryRate.class)) { + field.set(first, new FrameRateCategoryRate(16666667, 11111111)); + field.set(second, new FrameRateCategoryRate(11111111, 8333333)); } else { throw new IllegalArgumentException("Field " + field + " is not supported by this test, please add implementation of setting " |