display: support display config items for sdr and hdr blending

Support newly added interpolation type for nitsMap, sdrHdrRatioMap
and minimumHdrPercentOfScreen for sdr and hdr blending.

Bug: 196171661
Change-Id: Ic9832f49d701e520f587972a2fba2bd182cbd7a0
This commit is contained in:
Long Ling
2022-01-31 17:28:18 -08:00
parent f7ff96dd3f
commit 8982d22ee3
4 changed files with 184 additions and 19 deletions

View File

@@ -30,6 +30,8 @@ import android.util.Slog;
import android.util.Spline;
import android.view.DisplayAddress;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.R;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.server.display.config.BrightnessThresholds;
@@ -40,9 +42,12 @@ import com.android.server.display.config.DisplayConfiguration;
import com.android.server.display.config.DisplayQuirks;
import com.android.server.display.config.HbmTiming;
import com.android.server.display.config.HighBrightnessMode;
import com.android.server.display.config.Interpolation;
import com.android.server.display.config.NitsMap;
import com.android.server.display.config.Point;
import com.android.server.display.config.RefreshRateRange;
import com.android.server.display.config.SdrHdrRatioMap;
import com.android.server.display.config.SdrHdrRatioPoint;
import com.android.server.display.config.SensorDetails;
import com.android.server.display.config.ThermalStatus;
import com.android.server.display.config.ThermalThrottling;
@@ -70,6 +75,7 @@ import javax.xml.datatype.DatatypeConfigurationException;
*/
public class DisplayDeviceConfig {
private static final String TAG = "DisplayDeviceConfig";
private static final boolean DEBUG = false;
public static final float HIGH_BRIGHTNESS_MODE_UNSUPPORTED = Float.NaN;
@@ -86,6 +92,9 @@ public class DisplayDeviceConfig {
private static final String NO_SUFFIX_FORMAT = "%d";
private static final long STABLE_FLAG = 1L << 62;
private static final int INTERPOLATION_DEFAULT = 0;
private static final int INTERPOLATION_LINEAR = 1;
// Float.NaN (used as invalid for brightness) cannot be stored in config.xml
// so -2 is used instead
private static final float INVALID_BRIGHTNESS_IN_CONFIG = -2f;
@@ -99,6 +108,9 @@ public class DisplayDeviceConfig {
// Length of the ambient light horizon used to calculate short-term estimate of ambient light.
private static final int AMBIENT_LIGHT_SHORT_HORIZON_MILLIS = 2000;
@VisibleForTesting
static final float HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT = 0.5f;
private final Context mContext;
// The details of the ambient light sensor associated with this display.
@@ -114,6 +126,7 @@ public class DisplayDeviceConfig {
// config.xml. These are the raw values and just used for the dumpsys
private float[] mRawNits;
private float[] mRawBacklight;
private int mInterpolationType;
// These arrays are calculated from the raw arrays, but clamped to contain values equal to and
// between mBacklightMinimum and mBacklightMaximum. These three arrays should all be the same
@@ -142,6 +155,7 @@ public class DisplayDeviceConfig {
private Spline mBrightnessToBacklightSpline;
private Spline mBacklightToBrightnessSpline;
private Spline mBacklightToNitsSpline;
private Spline mNitsToBacklightSpline;
private List<String> mQuirks;
private boolean mIsHighBrightnessModeEnabled = false;
private HighBrightnessModeData mHbmData;
@@ -149,6 +163,7 @@ public class DisplayDeviceConfig {
private String mLoadedFrom = null;
private BrightnessThrottlingData mBrightnessThrottlingData;
private Spline mSdrToHdrRatioSpline;
private DisplayDeviceConfig(Context context) {
mContext = context;
@@ -332,6 +347,45 @@ public class DisplayDeviceConfig {
return mBacklightToNitsSpline.interpolate(backlight);
}
/**
* Calculate the HDR brightness for the specified SDR brightenss.
*
* @return the HDR brightness or BRIGHTNESS_INVALID when no mapping exists.
*/
public float getHdrBrightnessFromSdr(float brightness) {
if (mSdrToHdrRatioSpline == null) {
return PowerManager.BRIGHTNESS_INVALID;
}
float backlight = getBacklightFromBrightness(brightness);
float nits = getNitsFromBacklight(backlight);
if (nits == NITS_INVALID) {
return PowerManager.BRIGHTNESS_INVALID;
}
float ratio = mSdrToHdrRatioSpline.interpolate(nits);
float hdrNits = nits * ratio;
if (mNitsToBacklightSpline == null) {
return PowerManager.BRIGHTNESS_INVALID;
}
float hdrBacklight = mNitsToBacklightSpline.interpolate(hdrNits);
hdrBacklight = Math.max(mBacklightMinimum, Math.min(mBacklightMaximum, hdrBacklight));
float hdrBrightness = mBacklightToBrightnessSpline.interpolate(hdrBacklight);
if (DEBUG) {
Slog.d(TAG, "getHdrBrightnessFromSdr: sdr brightness " + brightness
+ " backlight " + backlight
+ " nits " + nits
+ " ratio " + ratio
+ " hdrNits " + hdrNits
+ " hdrBacklight " + hdrBacklight
+ " hdrBrightness " + hdrBrightness
);
}
return hdrBrightness;
}
/**
* Return an array of equal length to backlight and nits, that covers the entire system
* brightness range of 0.0-1.0.
@@ -444,15 +498,18 @@ public class DisplayDeviceConfig {
+ ", mNits=" + Arrays.toString(mNits)
+ ", mRawBacklight=" + Arrays.toString(mRawBacklight)
+ ", mRawNits=" + Arrays.toString(mRawNits)
+ ", mInterpolationType=" + mInterpolationType
+ ", mBrightness=" + Arrays.toString(mBrightness)
+ ", mBrightnessToBacklightSpline=" + mBrightnessToBacklightSpline
+ ", mBacklightToBrightnessSpline=" + mBacklightToBrightnessSpline
+ ", mNitsToBacklightSpline=" + mNitsToBacklightSpline
+ ", mBacklightMinimum=" + mBacklightMinimum
+ ", mBacklightMaximum=" + mBacklightMaximum
+ ", mBrightnessDefault=" + mBrightnessDefault
+ ", mQuirks=" + mQuirks
+ ", isHbmEnabled=" + mIsHighBrightnessModeEnabled
+ ", mHbmData=" + mHbmData
+ ", mSdrToHdrRatioSpline=" + mSdrToHdrRatioSpline
+ ", mBrightnessThrottlingData=" + mBrightnessThrottlingData
+ ", mBrightnessRampFastDecrease=" + mBrightnessRampFastDecrease
+ ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease
@@ -653,6 +710,7 @@ public class DisplayDeviceConfig {
float[] nits = new float[size];
float[] backlight = new float[size];
mInterpolationType = convertInterpolationType(map.getInterpolation());
int i = 0;
for (Point point : points) {
nits[i] = point.getNits().floatValue();
@@ -678,6 +736,40 @@ public class DisplayDeviceConfig {
constrainNitsAndBacklightArrays();
}
private Spline loadSdrHdrRatioMap(HighBrightnessMode hbmConfig) {
final SdrHdrRatioMap sdrHdrRatioMap = hbmConfig.getSdrHdrRatioMap_all();
if (sdrHdrRatioMap == null) {
return null;
}
final List<SdrHdrRatioPoint> points = sdrHdrRatioMap.getPoint();
final int size = points.size();
if (size <= 0) {
return null;
}
float[] nits = new float[size];
float[] ratios = new float[size];
int i = 0;
for (SdrHdrRatioPoint point : points) {
nits[i] = point.getSdrNits().floatValue();
if (i > 0) {
if (nits[i] < nits[i - 1]) {
Slog.e(TAG, "sdrHdrRatioMap must be non-decreasing, ignoring rest "
+ " of configuration. nits: " + nits[i] + " < "
+ nits[i - 1]);
return null;
}
}
ratios[i] = point.getHdrRatio().floatValue();
++i;
}
return Spline.createSpline(nits, ratios);
}
private void loadBrightnessThrottlingMap(DisplayConfiguration config) {
final ThermalThrottling throttlingConfig = config.getThermalThrottling();
if (throttlingConfig == null) {
@@ -823,9 +915,18 @@ public class DisplayDeviceConfig {
mBacklight[mBacklight.length - 1],
PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, mBacklight[i]);
}
mBrightnessToBacklightSpline = Spline.createSpline(mBrightness, mBacklight);
mBacklightToBrightnessSpline = Spline.createSpline(mBacklight, mBrightness);
mBacklightToNitsSpline = Spline.createSpline(mBacklight, mNits);
mBrightnessToBacklightSpline = mInterpolationType == INTERPOLATION_LINEAR
? Spline.createLinearSpline(mBrightness, mBacklight)
: Spline.createSpline(mBrightness, mBacklight);
mBacklightToBrightnessSpline = mInterpolationType == INTERPOLATION_LINEAR
? Spline.createLinearSpline(mBacklight, mBrightness)
: Spline.createSpline(mBacklight, mBrightness);
mBacklightToNitsSpline = mInterpolationType == INTERPOLATION_LINEAR
? Spline.createLinearSpline(mBacklight, mNits)
: Spline.createSpline(mBacklight, mNits);
mNitsToBacklightSpline = mInterpolationType == INTERPOLATION_LINEAR
? Spline.createLinearSpline(mNits, mBacklight)
: Spline.createSpline(mNits, mBacklight);
}
private void loadQuirks(DisplayConfiguration config) {
@@ -862,6 +963,20 @@ public class DisplayDeviceConfig {
mRefreshRateLimitations.add(new RefreshRateLimitation(
DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, min, max));
}
BigDecimal minHdrPctOfScreen = hbm.getMinimumHdrPercentOfScreen_all();
if (minHdrPctOfScreen != null) {
mHbmData.minimumHdrPercentOfScreen = minHdrPctOfScreen.floatValue();
if (mHbmData.minimumHdrPercentOfScreen > 1
|| mHbmData.minimumHdrPercentOfScreen < 0) {
Slog.w(TAG, "Invalid minimum HDR percent of screen: "
+ String.valueOf(mHbmData.minimumHdrPercentOfScreen));
mHbmData.minimumHdrPercentOfScreen = HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT;
}
} else {
mHbmData.minimumHdrPercentOfScreen = HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT;
}
mSdrToHdrRatioSpline = loadSdrHdrRatioMap(hbm);
}
}
@@ -1024,6 +1139,21 @@ public class DisplayDeviceConfig {
}
}
private int convertInterpolationType(Interpolation value) {
if (value == null) {
return INTERPOLATION_DEFAULT;
}
switch (value) {
case _default:
return INTERPOLATION_DEFAULT;
case linear:
return INTERPOLATION_LINEAR;
default:
Slog.wtf(TAG, "Unexpected Interpolation Type: " + value);
return INTERPOLATION_DEFAULT;
}
}
private void loadAmbientHorizonFromDdc(DisplayConfiguration config) {
final BigInteger configLongHorizon = config.getAmbientLightHorizonLong();
if (configLongHorizon != null) {
@@ -1088,11 +1218,15 @@ public class DisplayDeviceConfig {
/** Minimum time that HBM can be on before being enabled. */
public long timeMinMillis;
/** Minimum HDR video size to enter high brightness mode */
public float minimumHdrPercentOfScreen;
HighBrightnessModeData() {}
HighBrightnessModeData(float minimumLux, float transitionPoint, long timeWindowMillis,
long timeMaxMillis, long timeMinMillis,
@PowerManager.ThermalStatus int thermalStatusLimit, boolean allowInLowPowerMode) {
@PowerManager.ThermalStatus int thermalStatusLimit, boolean allowInLowPowerMode,
float minimumHdrPercentOfScreen) {
this.minimumLux = minimumLux;
this.transitionPoint = transitionPoint;
this.timeWindowMillis = timeWindowMillis;
@@ -1100,6 +1234,7 @@ public class DisplayDeviceConfig {
this.timeMinMillis = timeMinMillis;
this.thermalStatusLimit = thermalStatusLimit;
this.allowInLowPowerMode = allowInLowPowerMode;
this.minimumHdrPercentOfScreen = minimumHdrPercentOfScreen;
}
/**
@@ -1114,6 +1249,7 @@ public class DisplayDeviceConfig {
other.transitionPoint = transitionPoint;
other.thermalStatusLimit = thermalStatusLimit;
other.allowInLowPowerMode = allowInLowPowerMode;
other.minimumHdrPercentOfScreen = minimumHdrPercentOfScreen;
}
@Override
@@ -1126,6 +1262,7 @@ public class DisplayDeviceConfig {
+ ", timeMin: " + timeMinMillis + "ms"
+ ", thermalStatusLimit: " + thermalStatusLimit
+ ", allowInLowPowerMode: " + allowInLowPowerMode
+ ", minimumHdrPercentOfScreen: " + minimumHdrPercentOfScreen
+ "} ";
}
}

View File

@@ -831,7 +831,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
setUpAutoBrightness(mContext.getResources(), mHandler);
reloadReduceBrightColours();
mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
mDisplayDeviceConfig.getHighBrightnessModeData());
mDisplayDeviceConfig.getHighBrightnessModeData(),
new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
@Override
public float getHdrBrightnessFromSdr(float sdrBrightness) {
return mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness);
}
});
mBrightnessThrottler.resetThrottlingData(
mDisplayDeviceConfig.getBrightnessThrottlingData());
}
@@ -1697,6 +1703,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
return new HighBrightnessModeController(mHandler, info.width, info.height, displayToken,
displayUniqueId, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, hbmData,
new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
@Override
public float getHdrBrightnessFromSdr(float sdrBrightness) {
return mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness);
}
},
() -> {
sendUpdatePowerStateLocked();
postBrightnessChangeRunnable();

View File

@@ -58,11 +58,13 @@ class HighBrightnessModeController {
private static final boolean DEBUG = false;
private static final float HDR_PERCENT_OF_SCREEN_REQUIRED = 0.50f;
@VisibleForTesting
static final float HBM_TRANSITION_POINT_INVALID = Float.POSITIVE_INFINITY;
public interface HdrBrightnessDeviceConfig {
float getHdrBrightnessFromSdr(float sdrBrightness);
}
private final float mBrightnessMin;
private final float mBrightnessMax;
private final Handler mHandler;
@@ -76,6 +78,7 @@ class HighBrightnessModeController {
private HdrListener mHdrListener;
private HighBrightnessModeData mHbmData;
private HdrBrightnessDeviceConfig mHdrBrightnessCfg;
private IBinder mRegisteredDisplayToken;
private boolean mIsInAllowedAmbientRange = false;
@@ -115,16 +118,17 @@ class HighBrightnessModeController {
HighBrightnessModeController(Handler handler, int width, int height, IBinder displayToken,
String displayUniqueId, float brightnessMin, float brightnessMax,
HighBrightnessModeData hbmData, Runnable hbmChangeCallback, Context context) {
HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg,
Runnable hbmChangeCallback, Context context) {
this(new Injector(), handler, width, height, displayToken, displayUniqueId, brightnessMin,
brightnessMax, hbmData, hbmChangeCallback, context);
brightnessMax, hbmData, hdrBrightnessCfg, hbmChangeCallback, context);
}
@VisibleForTesting
HighBrightnessModeController(Injector injector, Handler handler, int width, int height,
IBinder displayToken, String displayUniqueId, float brightnessMin, float brightnessMax,
HighBrightnessModeData hbmData, Runnable hbmChangeCallback,
Context context) {
HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg,
Runnable hbmChangeCallback, Context context) {
mInjector = injector;
mContext = context;
mClock = injector.getClock();
@@ -138,7 +142,7 @@ class HighBrightnessModeController {
mRecalcRunnable = this::recalculateTimeAllowance;
mHdrListener = new HdrListener();
resetHbmData(width, height, displayToken, displayUniqueId, hbmData);
resetHbmData(width, height, displayToken, displayUniqueId, hbmData, hdrBrightnessCfg);
}
void setAutoBrightnessEnabled(int state) {
@@ -178,6 +182,13 @@ class HighBrightnessModeController {
}
float getHdrBrightnessValue() {
if (mHdrBrightnessCfg != null) {
float hdrBrightness = mHdrBrightnessCfg.getHdrBrightnessFromSdr(mBrightness);
if (hdrBrightness != PowerManager.BRIGHTNESS_INVALID) {
return hdrBrightness;
}
}
// For HDR brightness, we take the current brightness and scale it to the max. The reason
// we do this is because we want brightness to go to HBM max when it would normally go
// to normal max, meaning it should not wait to go to 10000 lux (or whatever the transition
@@ -250,10 +261,11 @@ class HighBrightnessModeController {
}
void resetHbmData(int width, int height, IBinder displayToken, String displayUniqueId,
HighBrightnessModeData hbmData) {
HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg) {
mWidth = width;
mHeight = height;
mHbmData = hbmData;
mHdrBrightnessCfg = hdrBrightnessCfg;
mDisplayStatsId = displayUniqueId.hashCode();
unregisterHdrListener();
@@ -598,8 +610,8 @@ class HighBrightnessModeController {
int maxW, int maxH, int flags) {
mHandler.post(() -> {
mIsHdrLayerPresent = numberOfHdrLayers > 0
&& (float) (maxW * maxH)
>= ((float) (mWidth * mHeight) * HDR_PERCENT_OF_SCREEN_REQUIRED);
&& (float) (maxW * maxH) >= ((float) (mWidth * mHeight)
* mHbmData.minimumHdrPercentOfScreen);
// Calling the brightness update so that we can recalculate
// brightness with HDR in mind.
onBrightnessChanged(mBrightness, mUnthrottledBrightness, mThrottlingReason);

View File

@@ -22,11 +22,14 @@ import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR;
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
import static com.android.server.display.AutomaticBrightnessController
.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE;
import static com.android.server.display.DisplayDeviceConfig.HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT;
import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID;
import static org.junit.Assert.assertEquals;
@@ -111,7 +114,8 @@ public class HighBrightnessModeControllerTest {
private static final HighBrightnessModeData DEFAULT_HBM_DATA =
new HighBrightnessModeData(MINIMUM_LUX, TRANSITION_POINT, TIME_WINDOW_MILLIS,
TIME_ALLOWED_IN_WINDOW_MILLIS, TIME_MINIMUM_AVAILABLE_TO_ENABLE_MILLIS,
THERMAL_STATUS_LIMIT, ALLOW_IN_LOW_POWER_MODE);
THERMAL_STATUS_LIMIT, ALLOW_IN_LOW_POWER_MODE,
HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT);
@Before
public void setUp() {
@@ -136,7 +140,7 @@ public class HighBrightnessModeControllerTest {
initHandler(null);
final HighBrightnessModeController hbmc = new HighBrightnessModeController(
mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken,
mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, () -> {}, mContextSpy);
mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {}, mContextSpy);
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f);
}
@@ -146,7 +150,7 @@ public class HighBrightnessModeControllerTest {
initHandler(null);
final HighBrightnessModeController hbmc = new HighBrightnessModeController(
mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken,
mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, () -> {}, mContextSpy);
mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {}, mContextSpy);
hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
@@ -679,7 +683,7 @@ public class HighBrightnessModeControllerTest {
initHandler(clock);
return new HighBrightnessModeController(mInjectorMock, mHandler, DISPLAY_WIDTH,
DISPLAY_HEIGHT, mDisplayToken, mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX,
DEFAULT_HBM_DATA, () -> {}, mContextSpy);
DEFAULT_HBM_DATA, null, () -> {}, mContextSpy);
}
private void initHandler(OffsettableClock clock) {