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

Commit 60cc1307 authored by Oleg Petšjonkin's avatar Oleg Petšjonkin Committed by Android (Google) Code Review
Browse files

Merge "Synthetic modes rework" into main

parents 76eb3847 49304fef
Loading
Loading
Loading
Loading
+70 −25
Original line number Diff line number Diff line
@@ -2429,6 +2429,18 @@ public final class Display {
     */
    @android.ravenwood.annotation.RavenwoodKeepWholeClass
    public static final class Mode implements Parcelable {
        /**
         * @hide
         */
        public static final int FLAG_ARR_RENDER_RATE = 1 << 0;

        /** @hide */
        @IntDef(flag = true, prefix = {"FLAG_"}, value = {
                FLAG_ARR_RENDER_RATE,
        })
        @Retention(RetentionPolicy.SOURCE)
        public @interface ModeFlags {}

        /**
         * @hide
         */
@@ -2440,6 +2452,9 @@ public final class Display {
        public static final int INVALID_MODE_ID = -1;

        private final int mModeId;
        private final int mParentModeId;
        @ModeFlags
        private final int mFlags;
        private final int mWidth;
        private final int mHeight;
        private final float mPeakRefreshRate;
@@ -2449,7 +2464,6 @@ public final class Display {
        @NonNull
        @HdrCapabilities.HdrType
        private final int[] mSupportedHdrTypes;
        private final boolean mIsSynthetic;

        /**
         * @hide
@@ -2483,22 +2497,23 @@ public final class Display {
         */
        public Mode(int modeId, int width, int height, float refreshRate, float vsyncRate,
                float[] alternativeRefreshRates, @HdrCapabilities.HdrType int[] supportedHdrTypes) {
            this(modeId, width, height, refreshRate, vsyncRate, false, alternativeRefreshRates,
                    supportedHdrTypes);
            this(modeId, INVALID_MODE_ID, 0, width, height, refreshRate, vsyncRate,
                    alternativeRefreshRates, supportedHdrTypes);
        }

        /**
         * @hide
         */
        public Mode(int modeId, int width, int height, float refreshRate, float vsyncRate,
                boolean isSynthetic, float[] alternativeRefreshRates,
                @HdrCapabilities.HdrType int[] supportedHdrTypes) {
        public Mode(int modeId, int parentModeId, @ModeFlags int flags,
                int width, int height, float refreshRate, float vsyncRate,
                float[] alternativeRefreshRates, @HdrCapabilities.HdrType int[] supportedHdrTypes) {
            mModeId = modeId;
            mParentModeId = parentModeId;
            mFlags = flags;
            mWidth = width;
            mHeight = height;
            mPeakRefreshRate = refreshRate;
            mVsyncRate = vsyncRate;
            mIsSynthetic = isSynthetic;
            mAlternativeRefreshRates =
                    Arrays.copyOf(alternativeRefreshRates, alternativeRefreshRates.length);
            Arrays.sort(mAlternativeRefreshRates);
@@ -2513,6 +2528,24 @@ public final class Display {
            return mModeId;
        }

        /**
         * Returns parent mode's id if the mode is derived from other Display.Mode.
         * Returns INVALID_MODE_ID in other cases.
         * @hide
         */
        public int getParentModeId() {
            return mParentModeId;
        }

        /**
         * Returns flags associated with this mode.
         * @hide
         */
        @ModeFlags
        public int getFlags() {
            return mFlags;
        }

        /**
         * Returns the physical width of the display in pixels when configured in this mode's
         * resolution.
@@ -2565,14 +2598,14 @@ public final class Display {
        }

        /**
         * Returns true if mode is synthetic and does not have corresponding
         * SurfaceControl.DisplayMode
         * Returns true if switching to this mode does not actually switch display mode on
         * SurfaceFlinger level
         * @hide
         */
        @SuppressWarnings("UnflaggedApi") // For testing only
        @TestApi
        public boolean isSynthetic() {
            return mIsSynthetic;
            return mParentModeId != INVALID_MODE_ID || (mFlags & FLAG_ARR_RENDER_RATE) != 0;
        }

        /**
@@ -2694,6 +2727,8 @@ public final class Display {
        public int hashCode() {
            int hash = 1;
            hash = hash * 17 + mModeId;
            hash = hash * 17 + mParentModeId;
            hash = hash * 17 + mFlags;
            hash = hash * 17 + mWidth;
            hash = hash * 17 + mHeight;
            hash = hash * 17 + Float.floatToIntBits(mPeakRefreshRate);
@@ -2707,11 +2742,12 @@ public final class Display {
        public String toString() {
            return new StringBuilder("{")
                    .append("id=").append(mModeId)
                    .append(", parentModeId=").append(mParentModeId)
                    .append(", flags=").append(flagsToString(mFlags))
                    .append(", width=").append(mWidth)
                    .append(", height=").append(mHeight)
                    .append(", fps=").append(mPeakRefreshRate)
                    .append(", vsync=").append(mVsyncRate)
                    .append(", synthetic=").append(mIsSynthetic)
                    .append(", alternativeRefreshRates=")
                    .append(Arrays.toString(mAlternativeRefreshRates))
                    .append(", supportedHdrTypes=")
@@ -2720,31 +2756,40 @@ public final class Display {
                    .toString();
        }

        private static String flagsToString(@ModeFlags int flags) {
            StringBuilder msg = new StringBuilder();
            if ((flags & FLAG_ARR_RENDER_RATE) != 0) {
                msg.append(", FLAG_ARR_RENDER_RATE");
            }
            return msg.toString();
        }

        @Override
        public int describeContents() {
            return 0;
        }

        private Mode(Parcel in) {
            this(in.readInt(), in.readInt(), in.readInt(), in.readFloat(), in.readFloat(),
                    in.readBoolean(), in.createFloatArray(), in.createIntArray());
            this(in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readInt(),
                    in.readFloat(), in.readFloat(), in.createFloatArray(), in.createIntArray());
        }

        @Override
        public void writeToParcel(Parcel out, int parcelableFlags) {
            out.writeInt(mModeId);
            out.writeInt(mParentModeId);
            out.writeInt(mFlags);
            out.writeInt(mWidth);
            out.writeInt(mHeight);
            out.writeFloat(mPeakRefreshRate);
            out.writeFloat(mVsyncRate);
            out.writeBoolean(mIsSynthetic);
            out.writeFloatArray(mAlternativeRefreshRates);
            out.writeIntArray(mSupportedHdrTypes);
        }

        @SuppressWarnings("hiding")
        public static final @android.annotation.NonNull Parcelable.Creator<Mode> CREATOR
                = new Parcelable.Creator<Mode>() {
        public static final @android.annotation.NonNull Parcelable.Creator<Mode> CREATOR =
                new Parcelable.Creator<>() {
                    @Override
                    public Mode createFromParcel(Parcel in) {
                        return new Mode(in);
+0 −17
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import android.view.SurfaceControl;
import com.android.server.display.feature.DisplayManagerFlags;

import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * A display adapter makes zero or more display devices available to the system
@@ -48,11 +47,6 @@ abstract class DisplayAdapter {
    public static final int DISPLAY_DEVICE_EVENT_CHANGED = 2;
    public static final int DISPLAY_DEVICE_EVENT_REMOVED = 3;

    /**
     * Used to generate globally unique display mode ids.
     */
    private static final AtomicInteger NEXT_DISPLAY_MODE_ID = new AtomicInteger(1);  // 0 = no mode.

    // Called with SyncRoot lock held.
    DisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler,
            Listener listener, String name, DisplayManagerFlags featureFlags) {
@@ -128,17 +122,6 @@ abstract class DisplayAdapter {
        mHandler.post(() -> mListener.onTraversalRequested());
    }

    public static Display.Mode createMode(int width, int height, float refreshRate) {
        return createMode(width, height, refreshRate, refreshRate, new float[0], new int[0]);
    }

    public static Display.Mode createMode(int width, int height, float refreshRate, float vsyncRate,
            float[] alternativeRefreshRates,
            @Display.HdrCapabilities.HdrType int[] supportedHdrTypes) {
        return new Display.Mode(NEXT_DISPLAY_MODE_ID.getAndIncrement(), width, height, refreshRate,
                vsyncRate, /* isSynthetic= */ false, alternativeRefreshRates, supportedHdrTypes);
    }

    static int getPowerModeForState(int state) {
        switch (state) {
            case Display.STATE_OFF:
+141 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 com.android.server.display;

import static com.android.server.display.DisplayDeviceConfig.DEFAULT_LOW_REFRESH_RATE;

import android.annotation.SuppressLint;
import android.view.Display;
import android.view.SurfaceControl;

import com.android.server.display.LocalDisplayAdapter.DisplayModeRecord;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class DisplayModeFactory {

    /**
     * Used to generate globally unique display mode ids.
     */
    private static final AtomicInteger NEXT_DISPLAY_MODE_ID = new AtomicInteger(1);  // 0 = no mode.


    private static final float FLOAT_TOLERANCE = 0.01f;
    private static final float SYNTHETIC_MODE_REFRESH_RATE = DEFAULT_LOW_REFRESH_RATE;
    private static final float SYNTHETIC_MODE_HIGH_BOUNDARY =
            SYNTHETIC_MODE_REFRESH_RATE + FLOAT_TOLERANCE;


    static Display.Mode createMode(int width, int height, float refreshRate) {
        return new Display.Mode(NEXT_DISPLAY_MODE_ID.getAndIncrement(),
                Display.Mode.INVALID_MODE_ID, 0,
                width, height, refreshRate, refreshRate,
                new float[0], new int[0]
        );
    }

    @SuppressLint("WrongConstant")
    static Display.Mode createMode(SurfaceControl.DisplayMode mode, float[] alternativeRefreshRates,
            boolean hasArrSupport, boolean syntheticModesV2Enabled) {
        int flags = 0;
        if (syntheticModesV2Enabled
                && hasArrSupport && mode.peakRefreshRate <= SYNTHETIC_MODE_HIGH_BOUNDARY) {
            flags |= Display.Mode.FLAG_ARR_RENDER_RATE;
        }

        return new Display.Mode(NEXT_DISPLAY_MODE_ID.getAndIncrement(),
                Display.Mode.INVALID_MODE_ID, flags,
                mode.width, mode.height, mode.peakRefreshRate, mode.vsyncRate,
                alternativeRefreshRates, mode.supportedHdrTypes
        );
    }

    @SuppressWarnings("MixedMutabilityReturnType")
    static List<DisplayModeRecord> createArrSyntheticModes(List<DisplayModeRecord> records,
            boolean hasArrSupport, boolean syntheticModesV2Enabled) {
        if (!syntheticModesV2Enabled) {
            return Collections.emptyList();
        }

        if (!hasArrSupport) {
            return Collections.emptyList();
        }

        List<Display.Mode> modesToSkipForArrSyntheticMode = new ArrayList<>();
        for (DisplayModeRecord record: records) {
            // already have < 60Hz mode, don't need to add synthetic
            if ((record.mMode.getFlags() & Display.Mode.FLAG_ARR_RENDER_RATE) != 0) {
                modesToSkipForArrSyntheticMode.add(record.mMode);
            }
        }

        List<Display.Mode> modesForArrSyntheticMode = new ArrayList<>();
        for (DisplayModeRecord record: records) {
            if (!is60HzAchievable(record.mMode)) {
                continue;
            }
            // already have < 60Hz mode, don't need to add synthetic
            if ((record.mMode.getFlags() & Display.Mode.FLAG_ARR_RENDER_RATE) != 0) {
                continue;
            }
            // already added OR should be skipped
            boolean skipAdding = hasMatichingForArr(modesForArrSyntheticMode, record.mMode)
                    || hasMatichingForArr(modesToSkipForArrSyntheticMode, record.mMode);

            if (!skipAdding) {
                modesForArrSyntheticMode.add(record.mMode);
            }
        }

        List<DisplayModeRecord> syntheticModes = new ArrayList<>();
        for (Display.Mode mode : modesForArrSyntheticMode) {
            syntheticModes.add(new DisplayModeRecord(
                    new Display.Mode(NEXT_DISPLAY_MODE_ID.getAndIncrement(),
                            mode.getModeId(), Display.Mode.FLAG_ARR_RENDER_RATE,
                            mode.getPhysicalWidth(), mode.getPhysicalHeight(),
                            SYNTHETIC_MODE_REFRESH_RATE, SYNTHETIC_MODE_REFRESH_RATE,
                            new float[0], mode.getSupportedHdrTypes()
                    )));
        }

        return syntheticModes;
    }

    private static boolean hasMatichingForArr(List<Display.Mode> modes, Display.Mode modeToMatch) {
        for (Display.Mode mode : modes) {
            if (matchingForSyntheticArr(modeToMatch, mode)) {
                return true;
            }
        }
        return false;
    }

    private static boolean is60HzAchievable(Display.Mode mode) {
        float divisor = mode.getVsyncRate() / SYNTHETIC_MODE_REFRESH_RATE;
        return Math.abs(divisor - Math.round(divisor)) < FLOAT_TOLERANCE;
    }

    private static  boolean matchingForSyntheticArr(Display.Mode mode1, Display.Mode mode2) {
        return mode1.getPhysicalWidth() == mode2.getPhysicalWidth()
                && mode1.getPhysicalHeight() == mode2.getPhysicalHeight()
                && Arrays.equals(mode1.getSupportedHdrTypes(), mode2.getSupportedHdrTypes());
    }
}
+32 −15
Original line number Diff line number Diff line
@@ -293,9 +293,8 @@ final class LocalDisplayAdapter extends DisplayAdapter {
        public boolean updateDisplayPropertiesLocked(SurfaceControl.StaticDisplayInfo staticInfo,
                SurfaceControl.DynamicDisplayInfo dynamicInfo,
                SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
            boolean changed = updateDisplayModesLocked(
                    dynamicInfo.supportedDisplayModes, dynamicInfo.preferredBootDisplayMode,
                    dynamicInfo.activeDisplayModeId, dynamicInfo.renderFrameRate, modeSpecs);
            boolean changed = updateDisplayModesLocked(dynamicInfo, modeSpecs);

            changed |= updateStaticInfo(staticInfo);
            changed |= updateColorModesLocked(dynamicInfo.supportedColorModes,
                    dynamicInfo.activeColorMode);
@@ -312,10 +311,15 @@ final class LocalDisplayAdapter extends DisplayAdapter {
            return changed;
        }

        public boolean updateDisplayModesLocked(
                SurfaceControl.DisplayMode[] displayModes, int preferredSfDisplayModeId,
                int activeSfDisplayModeId, float renderFrameRate,
        private boolean updateDisplayModesLocked(SurfaceControl.DynamicDisplayInfo dynamicInfo,
                SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
            SurfaceControl.DisplayMode[] displayModes = dynamicInfo.supportedDisplayModes;
            int preferredSfDisplayModeId = dynamicInfo.preferredBootDisplayMode;
            int activeSfDisplayModeId = dynamicInfo.activeDisplayModeId;
            float renderFrameRate = dynamicInfo.renderFrameRate;
            boolean hasArrSupport = dynamicInfo.hasArrSupport;
            boolean syntheticModesV2Enabled = getFeatureFlags().isSyntheticModesV2Enabled();

            mSfDisplayModes = Arrays.copyOf(displayModes, displayModes.length);
            mActiveSfDisplayMode = getModeById(displayModes, activeSfDisplayModeId);
            mAppVsyncOffsetNanos = mActiveSfDisplayMode.appVsyncOffsetNanos;
@@ -365,7 +369,9 @@ final class LocalDisplayAdapter extends DisplayAdapter {
                    for (int j = 0; j < alternativeRates.length; j++) {
                        alternativeRates[j] = alternativeRefreshRates.get(j);
                    }
                    record = new DisplayModeRecord(mode, alternativeRates);
                    Display.Mode displayMode = DisplayModeFactory.createMode(
                            mode, alternativeRates, hasArrSupport, syntheticModesV2Enabled);
                    record = new DisplayModeRecord(displayMode);
                    modesAdded = true;
                }
                records.add(record);
@@ -438,13 +444,18 @@ final class LocalDisplayAdapter extends DisplayAdapter {
                    sendTraversalRequestLocked();
                }
            }
            List<DisplayModeRecord> syntheticModes = DisplayModeFactory
                    .createArrSyntheticModes(records, hasArrSupport, syntheticModesV2Enabled);

            boolean recordsChanged = records.size() != mSupportedModes.size() || modesAdded;
            records.addAll(syntheticModes);

            boolean recordsChanged = records.size() != mSupportedModes.size() || modesAdded
                    || !syntheticModes.isEmpty();
            // If the records haven't changed then we're done here.
            if (!recordsChanged) {
                return activeModeChanged || preferredModeChanged || renderFrameRateChanged;
            }

            // Otherwise IDs changed and we need to update default/userPreferred/active mode IDs
            mSupportedModes.clear();
            for (DisplayModeRecord record : records) {
                mSupportedModes.put(record.mMode.getModeId(), record);
@@ -1151,7 +1162,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
                mSurfaceControlProxy.clearBootDisplayMode(getDisplayTokenLocked());
            } else {
                int preferredSfDisplayModeId = findSfDisplayModeIdLocked(
                        mUserPreferredMode.getModeId(), mDefaultModeGroup);
                        mUserPreferredMode, mDefaultModeGroup);
                mSurfaceControlProxy.setBootDisplayMode(getDisplayTokenLocked(),
                        preferredSfDisplayModeId);
            }
@@ -1405,6 +1416,14 @@ final class LocalDisplayAdapter extends DisplayAdapter {
            pw.println("mNitsToEvenDimmerStrength=" + mNitsToEvenDimmerStrength);
        }

        private int findSfDisplayModeIdLocked(Display.Mode mode, int modeGroup) {
            int modeId = mode.getModeId();
            if (mode.getParentModeId() != INVALID_MODE_ID) {
                modeId = mode.getParentModeId();
            }
            return findSfDisplayModeIdLocked(modeId, modeGroup);
        }

        private int findSfDisplayModeIdLocked(int displayModeId, int modeGroup) {
            int matchingSfDisplayModeId = INVALID_MODE_ID;
            DisplayModeRecord record = mSupportedModes.get(displayModeId);
@@ -1550,13 +1569,11 @@ final class LocalDisplayAdapter extends DisplayAdapter {
    /**
     * Keeps track of a display mode.
     */
    private static final class DisplayModeRecord {
    static final class DisplayModeRecord {
        public final Display.Mode mMode;

        DisplayModeRecord(SurfaceControl.DisplayMode mode,
                float[] alternativeRefreshRates) {
            mMode = createMode(mode.width, mode.height, mode.peakRefreshRate, mode.vsyncRate,
                    alternativeRefreshRates, mode.supportedHdrTypes);
        DisplayModeRecord(Display.Mode mode) {
            mMode = mode;
        }

        /**
+9 −5
Original line number Diff line number Diff line
@@ -231,14 +231,16 @@ final class LogicalDisplay {

    private final boolean mSyncedResolutionSwitchEnabled;

    private final boolean mSyntheticModesV2Enabled;

    private boolean mCanHostTasks;

    LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) {
        this(displayId, layerStack, primaryDisplayDevice, false);
        this(displayId, layerStack, primaryDisplayDevice, false, true);
    }

    LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice,
            boolean isSyncedResolutionSwitchEnabled) {
            boolean isSyncedResolutionSwitchEnabled, boolean syntheticModesV2Enabled) {
        mDisplayId = displayId;
        mLayerStack = layerStack;
        mPrimaryDisplayDevice = primaryDisplayDevice;
@@ -250,6 +252,7 @@ final class LogicalDisplay {
        mPowerThrottlingDataId = DisplayDeviceConfig.DEFAULT_ID;
        mBaseDisplayInfo.thermalBrightnessThrottlingDataId = mThermalBrightnessThrottlingDataId;
        mSyncedResolutionSwitchEnabled = isSyncedResolutionSwitchEnabled;
        mSyntheticModesV2Enabled = syntheticModesV2Enabled;

        // No need to initialize mCanHostTasks here; it's handled in
        // DisplayManagerService#setupLogicalDisplay().
@@ -535,9 +538,10 @@ final class LogicalDisplay {
            mBaseDisplayInfo.userPreferredModeId = deviceInfo.userPreferredModeId;
            mBaseDisplayInfo.supportedModes = Arrays.copyOf(
                    deviceInfo.supportedModes, deviceInfo.supportedModes.length);
            mBaseDisplayInfo.appsSupportedModes = syntheticModeManager.createAppSupportedModes(
                    config, mBaseDisplayInfo.supportedModes, mBaseDisplayInfo.hasArrSupport
            );
            mBaseDisplayInfo.appsSupportedModes = mSyntheticModesV2Enabled
                    ? Arrays.copyOf(deviceInfo.supportedModes, deviceInfo.supportedModes.length)
                    : syntheticModeManager.createAppSupportedModes(config,
                            mBaseDisplayInfo.supportedModes, mBaseDisplayInfo.hasArrSupport);
            mBaseDisplayInfo.colorMode = deviceInfo.colorMode;
            mBaseDisplayInfo.supportedColorModes = Arrays.copyOf(
                    deviceInfo.supportedColorModes,
Loading