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

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

Merge "Synthetic anistoropic modes support for external displays" into main

parents 81e2476c 02fe0102
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -2440,11 +2440,18 @@ public final class Display {
         * logical width and height without changing the mode reported to SurfaceFlinger.
         */
        public static final int FLAG_SIZE_OVERRIDE = 1 << 1;
        /**
         * @hide
         *
         * A synthetic display mode flag that indicates the mode has corrected anisotropy.
         */
        public static final int FLAG_ANISOTROPY_CORRECTION = 1 << 2;

        /** @hide */
        @IntDef(flag = true, prefix = {"FLAG_"}, value = {
                FLAG_ARR_RENDER_RATE,
                FLAG_SIZE_OVERRIDE,
                FLAG_ANISOTROPY_CORRECTION
        })
        @Retention(RetentionPolicy.SOURCE)
        public @interface ModeFlags {}
+10 −11
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.view.DisplayAddress;
import android.view.Surface;
import android.view.SurfaceControl;

import com.android.server.display.feature.flags.Flags;
import com.android.server.display.mode.DisplayModeDirector;

import java.io.PrintWriter;
@@ -81,9 +82,6 @@ abstract class DisplayDevice {
    // DEBUG STATE: Last device info which was written to the log, or null if none.
    // Do not use for any other purpose.
    DisplayDeviceInfo mDebugLastLoggedDeviceInfo;

    private boolean mIsAnisotropyCorrectionEnabled;

    DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken, String uniqueId,
            Context context) {
        mDisplayAdapter = displayAdapter;
@@ -158,7 +156,13 @@ abstract class DisplayDevice {
        DisplayDeviceInfo displayDeviceInfo = getDisplayDeviceInfoLocked();
        var width = displayDeviceInfo.width;
        var height = displayDeviceInfo.height;
        if (mIsAnisotropyCorrectionEnabled && displayDeviceInfo.type == Display.TYPE_EXTERNAL
        Display.Mode userMode = getUserPreferredDisplayModeLocked();
        if (displayDeviceInfo.type == Display.TYPE_EXTERNAL && userMode != null
                && (userMode.getFlags() & Display.Mode.FLAG_SIZE_OVERRIDE) != 0) {
            width = userMode.getPhysicalWidth();
            height = userMode.getPhysicalHeight();
        } else if (!Flags.enableAnisotropyCorrectedModes()
                && displayDeviceInfo.type == Display.TYPE_EXTERNAL
                && displayDeviceInfo.yDpi > 0 && displayDeviceInfo.xDpi > 0) {
            if (displayDeviceInfo.xDpi > displayDeviceInfo.yDpi * MAX_ANISOTROPY) {
                height = (int) (height * displayDeviceInfo.xDpi / displayDeviceInfo.yDpi + 0.5);
@@ -457,11 +461,6 @@ abstract class DisplayDevice {
        pw.println("mCurrentLayerStackRect=" + mCurrentLayerStackRect);
        pw.println("mCurrentDisplayRect=" + mCurrentDisplayRect);
        pw.println("mCurrentSurface=" + mCurrentSurface);
        pw.println("mIsAnisotropyCorrectionEnabled=" + mIsAnisotropyCorrectionEnabled);
    }

    void setAnisotropyCorrectionEnabled(boolean enabled) {
        mIsAnisotropyCorrectionEnabled = enabled;
    }

    /**
+62 −3
Original line number Diff line number Diff line
@@ -19,10 +19,12 @@ package com.android.server.display;
import static com.android.server.display.DisplayDeviceConfig.DEFAULT_LOW_REFRESH_RATE;

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

import com.android.server.display.LocalDisplayAdapter.DisplayModeRecord;
import com.android.server.display.feature.flags.Flags;

import java.util.ArrayList;
import java.util.Arrays;
@@ -101,8 +103,8 @@ public class DisplayModeFactory {
                continue;
            }
            // already added OR should be skipped
            boolean skipAdding = hasMatichingForArr(modesForArrSyntheticMode, record.mMode)
                    || hasMatichingForArr(modesToSkipForArrSyntheticMode, record.mMode);
            boolean skipAdding = hasMatchingForArr(modesForArrSyntheticMode, record.mMode)
                    || hasMatchingForArr(modesToSkipForArrSyntheticMode, record.mMode);

            if (!skipAdding) {
                modesForArrSyntheticMode.add(record.mMode);
@@ -123,7 +125,64 @@ public class DisplayModeFactory {
        return syntheticModes;
    }

    private static boolean hasMatichingForArr(List<Display.Mode> modes, Display.Mode modeToMatch) {
    /**
     * If the aspect ratio of the resolution of the display does not match the physical aspect
     * ratio of the display, then without this feature enabled, picture would appear stretched to
     * the user. This is because applications assume that they are rendered on square pixels
     * (meaning density of pixels in x and y directions are equal). This would result into circles
     * appearing as ellipses to the user.
     * 1. To compensate for non-square (anisotropic) pixels, this method will create synthetic modes
     * with more pixels for applications to render on, as if the pixels were square and occupied
     * the full display.
     * 2. SurfaceFlinger will squeeze this taller/wider surface into the available number of
     * physical pixels in the current display resolution.
     */
    @SuppressWarnings("MixedMutabilityReturnType")
    static List<DisplayModeRecord> createAnisotropyCorrectedModes(List<DisplayModeRecord> records,
            SparseArray<SurfaceControl.DisplayMode> modeIdToSfMode) {
        if (!Flags.enableAnisotropyCorrectedModes()) {
            return Collections.emptyList();
        }
        List<DisplayModeRecord> syntheticModes = new ArrayList<>();
        int modeFlag = Display.Mode.FLAG_SIZE_OVERRIDE | Display.Mode.FLAG_ANISOTROPY_CORRECTION;
        for (DisplayModeRecord record: records) {
            Display.Mode mode = record.mMode;
            SurfaceControl.DisplayMode sfMode = modeIdToSfMode.get(mode.getModeId());
            if (sfMode == null) {
                continue;
            }
            if (sfMode.xDpi <= 0 || sfMode.yDpi <= 0) {
                continue;
            }

            if (sfMode.xDpi > sfMode.yDpi * DisplayDevice.MAX_ANISOTROPY) { // "tall" pixels
                // scale up height in "logical" pixels
                int correctedHeight =
                        (int) (mode.getPhysicalHeight() * sfMode.xDpi / sfMode.yDpi + 0.5);
                syntheticModes.add(new DisplayModeRecord(
                        new Display.Mode(NEXT_DISPLAY_MODE_ID.getAndIncrement(),
                                mode.getModeId(), modeFlag,
                                mode.getPhysicalWidth(), correctedHeight,
                                mode.getRefreshRate(), mode.getVsyncRate(),
                                mode.getAlternativeRefreshRates(), mode.getSupportedHdrTypes()
                        )));
            } else if (sfMode.yDpi > sfMode.xDpi * DisplayDevice.MAX_ANISOTROPY) { // "wide" pixels
                // scale up width in "logical" pixels
                int correctedWidth =
                        (int) (mode.getPhysicalWidth() * sfMode.yDpi / sfMode.xDpi + 0.5);
                syntheticModes.add(new DisplayModeRecord(
                        new Display.Mode(NEXT_DISPLAY_MODE_ID.getAndIncrement(),
                                mode.getModeId(), modeFlag,
                                correctedWidth, mode.getPhysicalHeight(),
                                mode.getRefreshRate(), mode.getVsyncRate(),
                                mode.getAlternativeRefreshRates(), mode.getSupportedHdrTypes()
                        )));
            }
        }
        return syntheticModes;
    }

    private static boolean hasMatchingForArr(List<Display.Mode> modes, Display.Mode modeToMatch) {
        for (Display.Mode mode : modes) {
            if (matchingForSyntheticArr(modeToMatch, mode)) {
                return true;
+9 −3
Original line number Diff line number Diff line
@@ -331,6 +331,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {

            // Build an updated list of all existing modes.
            ArrayList<DisplayModeRecord> records = new ArrayList<>();
            SparseArray<SurfaceControl.DisplayMode> modeIdToSfMode = new SparseArray<>();
            boolean modesAdded = false;
            for (int i = 0; i < displayModes.length; i++) {
                SurfaceControl.DisplayMode mode = displayModes[i];
@@ -377,6 +378,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
                    modesAdded = true;
                }
                records.add(record);
                modeIdToSfMode.append(record.mMode.getModeId(), mode);
            }

            // Get the currently active mode
@@ -446,9 +448,13 @@ final class LocalDisplayAdapter extends DisplayAdapter {
                    sendTraversalRequestLocked();
                }
            }
            List<DisplayModeRecord> syntheticModes = DisplayModeFactory
                    .createArrSyntheticModes(records, hasArrSupport, syntheticModesV2Enabled);

            List<DisplayModeRecord> syntheticModes = new ArrayList<>();
            syntheticModes.addAll(DisplayModeFactory
                    .createArrSyntheticModes(records, hasArrSupport, syntheticModesV2Enabled));
            if (!isInternal) {
                syntheticModes.addAll(DisplayModeFactory
                        .createAnisotropyCorrectedModes(records, modeIdToSfMode));
            }
            records.addAll(syntheticModes);

            boolean recordsChanged = records.size() != mSupportedModes.size() || modesAdded
+22 −24
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceControl;

import com.android.server.display.feature.flags.Flags;
import com.android.server.display.layout.Layout;
import com.android.server.display.mode.DisplayModeDirector;
import com.android.server.display.mode.SyntheticModeManager;
@@ -217,20 +218,11 @@ final class LogicalDisplay {
            new SparseArray<>();

    /**
     * If the aspect ratio of the resolution of the display does not match the physical aspect
     * ratio of the display, then without this feature enabled, picture would appear stretched to
     * the user. This is because applications assume that they are rendered on square pixels
     * (meaning density of pixels in x and y directions are equal). This would result into circles
     * appearing as ellipses to the user.
     * To compensate for non-square (anisotropic) pixels, if this feature is enabled:
     * 1. LogicalDisplay will add more pixels for the applications to render on, as if the pixels
     * were square and occupied the full display.
     * 2. SurfaceFlinger will squeeze this taller/wider surface into the available number of
     * physical pixels in the current display resolution.
     * 3. If a setting on the display itself is set to "fill the entire display panel" then the
     * display will stretch the pixels to fill the display fully.
     * Modes with corrected anisotropy are supplied by LocalDisplayAdapter, no anisotropy
     * calculation is needed on LocalDisplay side. User selected resolution is scaled to fit
     * physical resolution of the display.
     */
    private boolean mIsAnisotropyCorrectionEnabled;
    private final boolean mIsAnisotropicModesEnabled;

    private final boolean mSyncedResolutionSwitchEnabled;

@@ -260,15 +252,11 @@ final class LogicalDisplay {
        mSyntheticModesV2Enabled = syntheticModesV2Enabled;
        mSizeOverrideEnabled = sizeOverrideEnabled;

        mIsAnisotropicModesEnabled = Flags.enableAnisotropyCorrectedModes();
        // No need to initialize mCanHostTasks here; it's handled in
        // DisplayManagerService#setupLogicalDisplay().
    }

    public void setAnisotropyCorrectionEnabled(boolean enabled) {
        mIsAnisotropyCorrectionEnabled = enabled;
        mPrimaryDisplayDevice.setAnisotropyCorrectionEnabled(enabled);
    }

    public void setDevicePositionLocked(int position) {
        if (mDevicePosition != position) {
            mDevicePosition = position;
@@ -444,9 +432,6 @@ final class LogicalDisplay {
            return;
        }

        // TODO(b/375563565): Implement per-display setting to enable/disable anisotropy correction.
        setAnisotropyCorrectionEnabled(true);

        // Bootstrap the logical display using its associated primary physical display.
        // We might use more elaborate configurations later.  It's possible that the
        // configuration of several physical displays might be used to determine the
@@ -529,7 +514,8 @@ final class LogicalDisplay {
            int maskedWidth = currentWidth - maskingInsets.left - maskingInsets.right;
            int maskedHeight = currentHeight - maskingInsets.top - maskingInsets.bottom;

            if (mIsAnisotropyCorrectionEnabled && deviceInfo.type == Display.TYPE_EXTERNAL
            if (!mIsAnisotropicModesEnabled
                    && deviceInfo.type == Display.TYPE_EXTERNAL
                        && currentXDpi > 0 && currentYDpi > 0) {
                if (currentXDpi > currentYDpi * DisplayDevice.MAX_ANISOTROPY) {
                    maskedHeight = (int) (maskedHeight * currentXDpi / currentYDpi + 0.5);
@@ -813,7 +799,7 @@ final class LogicalDisplay {
        var displayLogicalWidth = displayInfo.logicalWidth;
        var displayLogicalHeight = displayInfo.logicalHeight;

        if (mIsAnisotropyCorrectionEnabled && displayDeviceInfo.type == Display.TYPE_EXTERNAL
        if (!mIsAnisotropicModesEnabled && displayDeviceInfo.type == Display.TYPE_EXTERNAL
                    && displayDeviceInfo.xDpi > 0 && displayDeviceInfo.yDpi > 0) {
            if (displayDeviceInfo.xDpi > displayDeviceInfo.yDpi * DisplayDevice.MAX_ANISOTROPY) {
                var scalingFactor = displayDeviceInfo.yDpi / displayDeviceInfo.xDpi;
@@ -835,6 +821,16 @@ final class LogicalDisplay {
            }
        }

        // For size override modes we want to scale logical width and height to physical/user mode
        // width and height ratio
        Display.Mode userMode = getUserPreferredModeForSizeOverrideLocked(displayDeviceInfo);
        if (mIsAnisotropicModesEnabled && displayDeviceInfo.type == Display.TYPE_EXTERNAL
                && userMode != null
                && (userMode.getFlags() & Display.Mode.FLAG_SIZE_OVERRIDE) != 0) {
            displayLogicalWidth = displayLogicalWidth * physWidth / userMode.getPhysicalWidth();
            displayLogicalHeight = displayLogicalHeight * physHeight / userMode.getPhysicalHeight();
        }

        // Determine whether the width or height is more constrained to be scaled.
        //    physWidth / displayInfo.logicalWidth    => letter box
        // or physHeight / displayInfo.logicalHeight  => pillar box
@@ -842,8 +838,10 @@ final class LogicalDisplay {
        // We avoid a division (and possible floating point imprecision) here by
        // multiplying the fractions by the product of their denominators before
        // comparing them.

        int displayRectWidth, displayRectHeight;
        if ((displayInfo.flags & Display.FLAG_SCALING_DISABLED) != 0 || mDisplayScalingDisabled) {
        if ((displayInfo.flags & Display.FLAG_SCALING_DISABLED) != 0
                || mDisplayScalingDisabled) {
            displayRectWidth = displayLogicalWidth;
            displayRectHeight = displayLogicalHeight;
        } else if (physWidth * displayLogicalHeight
Loading