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

Commit 02fe0102 authored by Oleg Petsjonkin's avatar Oleg Petsjonkin Committed by Oleg Petšjonkin
Browse files

Synthetic anistoropic modes support for external displays

Synthetic ansiotropic modes are not changing display mode,
but applying size to LogicalDisplay instead, similar to ag/35126291

Bug: 375563565
Test: atest DisplayDeviceTest
Flag: com.android.server.display.feature.flags.enable_anisotropy_corrected_modes

Change-Id: I538ba44d749a21b1dad45161e3fac67caf956518
parent 2a22330b
Loading
Loading
Loading
Loading
+7 −0
Original line number Original line Diff line number Diff line
@@ -2440,11 +2440,18 @@ public final class Display {
         * logical width and height without changing the mode reported to SurfaceFlinger.
         * logical width and height without changing the mode reported to SurfaceFlinger.
         */
         */
        public static final int FLAG_SIZE_OVERRIDE = 1 << 1;
        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 */
        /** @hide */
        @IntDef(flag = true, prefix = {"FLAG_"}, value = {
        @IntDef(flag = true, prefix = {"FLAG_"}, value = {
                FLAG_ARR_RENDER_RATE,
                FLAG_ARR_RENDER_RATE,
                FLAG_SIZE_OVERRIDE,
                FLAG_SIZE_OVERRIDE,
                FLAG_ANISOTROPY_CORRECTION
        })
        })
        @Retention(RetentionPolicy.SOURCE)
        @Retention(RetentionPolicy.SOURCE)
        public @interface ModeFlags {}
        public @interface ModeFlags {}
+10 −11
Original line number Original line Diff line number Diff line
@@ -33,6 +33,7 @@ import android.view.DisplayAddress;
import android.view.Surface;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl;


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


import java.io.PrintWriter;
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.
    // DEBUG STATE: Last device info which was written to the log, or null if none.
    // Do not use for any other purpose.
    // Do not use for any other purpose.
    DisplayDeviceInfo mDebugLastLoggedDeviceInfo;
    DisplayDeviceInfo mDebugLastLoggedDeviceInfo;

    private boolean mIsAnisotropyCorrectionEnabled;

    DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken, String uniqueId,
    DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken, String uniqueId,
            Context context) {
            Context context) {
        mDisplayAdapter = displayAdapter;
        mDisplayAdapter = displayAdapter;
@@ -158,7 +156,13 @@ abstract class DisplayDevice {
        DisplayDeviceInfo displayDeviceInfo = getDisplayDeviceInfoLocked();
        DisplayDeviceInfo displayDeviceInfo = getDisplayDeviceInfoLocked();
        var width = displayDeviceInfo.width;
        var width = displayDeviceInfo.width;
        var height = displayDeviceInfo.height;
        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) {
                && displayDeviceInfo.yDpi > 0 && displayDeviceInfo.xDpi > 0) {
            if (displayDeviceInfo.xDpi > displayDeviceInfo.yDpi * MAX_ANISOTROPY) {
            if (displayDeviceInfo.xDpi > displayDeviceInfo.yDpi * MAX_ANISOTROPY) {
                height = (int) (height * displayDeviceInfo.xDpi / displayDeviceInfo.yDpi + 0.5);
                height = (int) (height * displayDeviceInfo.xDpi / displayDeviceInfo.yDpi + 0.5);
@@ -457,11 +461,6 @@ abstract class DisplayDevice {
        pw.println("mCurrentLayerStackRect=" + mCurrentLayerStackRect);
        pw.println("mCurrentLayerStackRect=" + mCurrentLayerStackRect);
        pw.println("mCurrentDisplayRect=" + mCurrentDisplayRect);
        pw.println("mCurrentDisplayRect=" + mCurrentDisplayRect);
        pw.println("mCurrentSurface=" + mCurrentSurface);
        pw.println("mCurrentSurface=" + mCurrentSurface);
        pw.println("mIsAnisotropyCorrectionEnabled=" + mIsAnisotropyCorrectionEnabled);
    }

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


    /**
    /**
+62 −3
Original line number Original line 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 static com.android.server.display.DisplayDeviceConfig.DEFAULT_LOW_REFRESH_RATE;


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


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


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


            if (!skipAdding) {
            if (!skipAdding) {
                modesForArrSyntheticMode.add(record.mMode);
                modesForArrSyntheticMode.add(record.mMode);
@@ -123,7 +125,64 @@ public class DisplayModeFactory {
        return syntheticModes;
        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) {
        for (Display.Mode mode : modes) {
            if (matchingForSyntheticArr(modeToMatch, mode)) {
            if (matchingForSyntheticArr(modeToMatch, mode)) {
                return true;
                return true;
+9 −3
Original line number Original line Diff line number Diff line
@@ -331,6 +331,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {


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


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

                    .createArrSyntheticModes(records, hasArrSupport, syntheticModesV2Enabled));
            if (!isInternal) {
                syntheticModes.addAll(DisplayModeFactory
                        .createAnisotropyCorrectedModes(records, modeIdToSfMode));
            }
            records.addAll(syntheticModes);
            records.addAll(syntheticModes);


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


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


    /**
    /**
     * If the aspect ratio of the resolution of the display does not match the physical aspect
     * Modes with corrected anisotropy are supplied by LocalDisplayAdapter, no anisotropy
     * ratio of the display, then without this feature enabled, picture would appear stretched to
     * calculation is needed on LocalDisplay side. User selected resolution is scaled to fit
     * the user. This is because applications assume that they are rendered on square pixels
     * physical resolution of the display.
     * (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.
     */
     */
    private boolean mIsAnisotropyCorrectionEnabled;
    private final boolean mIsAnisotropicModesEnabled;


    private final boolean mSyncedResolutionSwitchEnabled;
    private final boolean mSyncedResolutionSwitchEnabled;


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


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


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

    public void setDevicePositionLocked(int position) {
    public void setDevicePositionLocked(int position) {
        if (mDevicePosition != position) {
        if (mDevicePosition != position) {
            mDevicePosition = position;
            mDevicePosition = position;
@@ -443,9 +431,6 @@ final class LogicalDisplay {
            return;
            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.
        // Bootstrap the logical display using its associated primary physical display.
        // We might use more elaborate configurations later.  It's possible that the
        // We might use more elaborate configurations later.  It's possible that the
        // configuration of several physical displays might be used to determine the
        // configuration of several physical displays might be used to determine the
@@ -528,7 +513,8 @@ final class LogicalDisplay {
            int maskedWidth = currentWidth - maskingInsets.left - maskingInsets.right;
            int maskedWidth = currentWidth - maskingInsets.left - maskingInsets.right;
            int maskedHeight = currentHeight - maskingInsets.top - maskingInsets.bottom;
            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) {
                        && currentXDpi > 0 && currentYDpi > 0) {
                if (currentXDpi > currentYDpi * DisplayDevice.MAX_ANISOTROPY) {
                if (currentXDpi > currentYDpi * DisplayDevice.MAX_ANISOTROPY) {
                    maskedHeight = (int) (maskedHeight * currentXDpi / currentYDpi + 0.5);
                    maskedHeight = (int) (maskedHeight * currentXDpi / currentYDpi + 0.5);
@@ -812,7 +798,7 @@ final class LogicalDisplay {
        var displayLogicalWidth = displayInfo.logicalWidth;
        var displayLogicalWidth = displayInfo.logicalWidth;
        var displayLogicalHeight = displayInfo.logicalHeight;
        var displayLogicalHeight = displayInfo.logicalHeight;


        if (mIsAnisotropyCorrectionEnabled && displayDeviceInfo.type == Display.TYPE_EXTERNAL
        if (!mIsAnisotropicModesEnabled && displayDeviceInfo.type == Display.TYPE_EXTERNAL
                    && displayDeviceInfo.xDpi > 0 && displayDeviceInfo.yDpi > 0) {
                    && displayDeviceInfo.xDpi > 0 && displayDeviceInfo.yDpi > 0) {
            if (displayDeviceInfo.xDpi > displayDeviceInfo.yDpi * DisplayDevice.MAX_ANISOTROPY) {
            if (displayDeviceInfo.xDpi > displayDeviceInfo.yDpi * DisplayDevice.MAX_ANISOTROPY) {
                var scalingFactor = displayDeviceInfo.yDpi / displayDeviceInfo.xDpi;
                var scalingFactor = displayDeviceInfo.yDpi / displayDeviceInfo.xDpi;
@@ -834,6 +820,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.
        // Determine whether the width or height is more constrained to be scaled.
        //    physWidth / displayInfo.logicalWidth    => letter box
        //    physWidth / displayInfo.logicalWidth    => letter box
        // or physHeight / displayInfo.logicalHeight  => pillar box
        // or physHeight / displayInfo.logicalHeight  => pillar box
@@ -841,8 +837,10 @@ final class LogicalDisplay {
        // We avoid a division (and possible floating point imprecision) here by
        // We avoid a division (and possible floating point imprecision) here by
        // multiplying the fractions by the product of their denominators before
        // multiplying the fractions by the product of their denominators before
        // comparing them.
        // comparing them.

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