[SurfaceFlinger] Add BT2100_PQ and BT2100_HLG color mode.
When hardware composer has native HDR10/HLG support, SurfaceFlinger will always
pass the layer to hardware composer. When hardware composer doesn't have native
HDR10/HLG support, but has BT2100_PQ or BT2100_HLG color mode with render
intent, SurfaceFlinger will always set the color mode to BT2100_PQ and
BT2100_HLG respectively, and set the render intent to TONE_MAP_ENHANCE if
supported, or TONE_MAP_COLORIMETRIC. Otherwise, SurfaceFlinger will set the
color mode to Display P3 and simulate PQ/HLG in RenderEngine.
Since SurfaceFlinger now can simulate HLG support in Display P3 mode, when apps
query HDR capability from platform, we also return HLG support.
BUG: 73825729
Test: build, flash
Change-Id: I53696360f2b3d986aa9191ff42866e275ba4fd0b
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 225aad1..c4a1191 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1021,25 +1021,12 @@
}
void SurfaceFlinger::setActiveColorModeInternal(const sp<DisplayDevice>& hw,
- ColorMode mode, Dataspace dataSpace) {
+ ColorMode mode, Dataspace dataSpace,
+ RenderIntent renderIntent) {
ColorMode currentMode = hw->getActiveColorMode();
Dataspace currentDataSpace = hw->getCompositionDataSpace();
RenderIntent currentRenderIntent = hw->getActiveRenderIntent();
- // Natural Mode means it's color managed and the color must be right,
- // thus we pick RenderIntent::COLORIMETRIC as render intent.
- // Native Mode means the display is not color managed, and whichever
- // render intent is picked doesn't matter, thus return
- // RenderIntent::COLORIMETRIC as default here.
- RenderIntent renderIntent = RenderIntent::COLORIMETRIC;
-
- // In Auto Color Mode, we want to strech to panel color space, right now
- // only the built-in display supports it.
- if (mDisplayColorSetting == DisplayColorSetting::ENHANCED && mBuiltinDisplaySupportsEnhance &&
- hw->isPrimary()) {
- renderIntent = RenderIntent::ENHANCE;
- }
-
if (mode == currentMode && dataSpace == currentDataSpace &&
renderIntent == currentRenderIntent) {
return;
@@ -1089,7 +1076,8 @@
ALOGW("Attempt to set active color mode %s %d for virtual display",
decodeColorMode(mMode).c_str(), mMode);
} else {
- mFlinger.setActiveColorModeInternal(hw, mMode, Dataspace::UNKNOWN);
+ mFlinger.setActiveColorModeInternal(hw, mMode, Dataspace::UNKNOWN,
+ RenderIntent::COLORIMETRIC);
}
return true;
}
@@ -1870,38 +1858,41 @@
}
}
-// Returns a dataspace that fits all visible layers. The returned dataspace
+// Returns a data space that fits all visible layers. The returned data space
// can only be one of
-//
// - Dataspace::V0_SRGB
// - Dataspace::DISPLAY_P3
// - Dataspace::V0_SCRGB_LINEAR
-// TODO(b/73825729) Add BT2020 data space.
-ui::Dataspace SurfaceFlinger::getBestDataspace(
- const sp<const DisplayDevice>& displayDevice) const {
- Dataspace bestDataspace = Dataspace::V0_SRGB;
+// The returned HDR data space is one of
+// - Dataspace::UNKNOWN
+// - Dataspace::BT2020_HLG
+// - Dataspace::BT2020_PQ
+Dataspace SurfaceFlinger::getBestDataspace(
+ const sp<const DisplayDevice>& displayDevice, Dataspace* outHdrDataSpace) const {
+ Dataspace bestDataSpace = Dataspace::V0_SRGB;
+ *outHdrDataSpace = Dataspace::UNKNOWN;
+
for (const auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
switch (layer->getDataSpace()) {
case Dataspace::V0_SCRGB:
case Dataspace::V0_SCRGB_LINEAR:
- // return immediately
- return Dataspace::V0_SCRGB_LINEAR;
- case Dataspace::DISPLAY_P3:
- bestDataspace = Dataspace::DISPLAY_P3;
+ bestDataSpace = Dataspace::V0_SCRGB_LINEAR;
break;
- // Historically, HDR dataspaces are ignored by SurfaceFlinger. But
- // since SurfaceFlinger simulates HDR support now, it should honor
- // them unless there is also native support.
+ case Dataspace::DISPLAY_P3:
+ if (bestDataSpace == Dataspace::V0_SRGB) {
+ bestDataSpace = Dataspace::DISPLAY_P3;
+ }
+ break;
case Dataspace::BT2020_PQ:
case Dataspace::BT2020_ITU_PQ:
- if (!displayDevice->hasHDR10Support()) {
- return Dataspace::V0_SCRGB_LINEAR;
- }
+ *outHdrDataSpace = Dataspace::BT2020_PQ;
break;
case Dataspace::BT2020_HLG:
case Dataspace::BT2020_ITU_HLG:
- if (!displayDevice->hasHLGSupport()) {
- return Dataspace::V0_SCRGB_LINEAR;
+ // When there's mixed PQ content and HLG content, we set the HDR
+ // data space to be BT2020_PQ and convert HLG to PQ.
+ if (*outHdrDataSpace == Dataspace::UNKNOWN) {
+ *outHdrDataSpace = Dataspace::BT2020_HLG;
}
break;
default:
@@ -1909,29 +1900,119 @@
}
}
- return bestDataspace;
+ return bestDataSpace;
}
// Pick the ColorMode / Dataspace for the display device.
-// TODO(b/73825729) Add BT2020 color mode.
void SurfaceFlinger::pickColorMode(const sp<DisplayDevice>& displayDevice,
- ColorMode* outMode, Dataspace* outDataSpace) const {
+ ColorMode* outMode, Dataspace* outDataSpace,
+ RenderIntent* outRenderIntent) const {
if (mDisplayColorSetting == DisplayColorSetting::UNMANAGED) {
*outMode = ColorMode::NATIVE;
*outDataSpace = Dataspace::UNKNOWN;
+ *outRenderIntent = RenderIntent::COLORIMETRIC;
return;
}
- switch (getBestDataspace(displayDevice)) {
- case Dataspace::DISPLAY_P3:
- case Dataspace::V0_SCRGB_LINEAR:
+ Dataspace hdrDataSpace;
+ Dataspace bestDataSpace = getBestDataspace(displayDevice, &hdrDataSpace);
+
+ if (hdrDataSpace == Dataspace::BT2020_PQ) {
+ // Hardware composer can handle BT2100 ColorMode only when
+ // - colorimetrical tone mapping is supported, or
+ // - Auto mode is turned on and enhanced tone mapping is supported.
+ if (displayDevice->hasBT2100PQColorimetricSupport() ||
+ (mDisplayColorSetting == DisplayColorSetting::ENHANCED &&
+ displayDevice->hasBT2100PQEnhanceSupport())) {
+ *outMode = ColorMode::BT2100_PQ;
+ *outDataSpace = Dataspace::BT2020_PQ;
+ } else if (displayDevice->hasHDR10Support()) {
+ // Legacy HDR support. HDR layers are treated as UNKNOWN layers.
+ hdrDataSpace = Dataspace::UNKNOWN;
+ } else {
+ // Simulate PQ through RenderEngine, pick DISPLAY_P3 color mode.
*outMode = ColorMode::DISPLAY_P3;
*outDataSpace = Dataspace::DISPLAY_P3;
- break;
+ }
+ } else if (hdrDataSpace == Dataspace::BT2020_HLG) {
+ if (displayDevice->hasBT2100HLGColorimetricSupport() ||
+ (mDisplayColorSetting == DisplayColorSetting::ENHANCED &&
+ displayDevice->hasBT2100HLGEnhanceSupport())) {
+ *outMode = ColorMode::BT2100_HLG;
+ *outDataSpace = Dataspace::BT2020_HLG;
+ } else if (displayDevice->hasHLGSupport()) {
+ // Legacy HDR support. HDR layers are treated as UNKNOWN layers.
+ hdrDataSpace = Dataspace::UNKNOWN;
+ } else {
+ // Simulate HLG through RenderEngine, pick DISPLAY_P3 color mode.
+ *outMode = ColorMode::DISPLAY_P3;
+ *outDataSpace = Dataspace::DISPLAY_P3;
+ }
+ }
+
+ // At this point, there's no HDR layer.
+ if (hdrDataSpace == Dataspace::UNKNOWN) {
+ switch (bestDataSpace) {
+ case Dataspace::DISPLAY_P3:
+ case Dataspace::V0_SCRGB_LINEAR:
+ *outMode = ColorMode::DISPLAY_P3;
+ *outDataSpace = Dataspace::DISPLAY_P3;
+ break;
+ default:
+ *outMode = ColorMode::SRGB;
+ *outDataSpace = Dataspace::V0_SRGB;
+ break;
+ }
+ }
+ *outRenderIntent = pickRenderIntent(displayDevice, *outMode);
+}
+
+RenderIntent SurfaceFlinger::pickRenderIntent(const sp<DisplayDevice>& displayDevice,
+ ColorMode colorMode) const {
+ // Native Mode means the display is not color managed, and whichever
+ // render intent is picked doesn't matter, thus return
+ // RenderIntent::COLORIMETRIC as default here.
+ if (mDisplayColorSetting == DisplayColorSetting::UNMANAGED) {
+ return RenderIntent::COLORIMETRIC;
+ }
+
+ // In Auto Color Mode, we want to strech to panel color space, right now
+ // only the built-in display supports it.
+ if (mDisplayColorSetting == DisplayColorSetting::ENHANCED &&
+ mBuiltinDisplaySupportsEnhance && displayDevice->isPrimary()) {
+ switch (colorMode) {
+ case ColorMode::DISPLAY_P3:
+ case ColorMode::SRGB:
+ return RenderIntent::ENHANCE;
+ // In Auto Color Mode, BT2100_PQ and BT2100_HLG will only be picked
+ // when TONE_MAP_ENHANCE or TONE_MAP_COLORIMETRIC is supported.
+ // If TONE_MAP_ENHANCE is not supported, fall back to TONE_MAP_COLORIMETRIC.
+ case ColorMode::BT2100_PQ:
+ return displayDevice->hasBT2100PQEnhanceSupport() ?
+ RenderIntent::TONE_MAP_ENHANCE : RenderIntent::TONE_MAP_COLORIMETRIC;
+ case ColorMode::BT2100_HLG:
+ return displayDevice->hasBT2100HLGEnhanceSupport() ?
+ RenderIntent::TONE_MAP_ENHANCE : RenderIntent::TONE_MAP_COLORIMETRIC;
+ // This statement shouldn't be reached, switch cases will always
+ // cover all possible ColorMode returned by pickColorMode.
+ default:
+ return RenderIntent::COLORIMETRIC;
+ }
+ }
+
+ // Either enhance is not supported or we are in natural mode.
+
+ // Natural Mode means it's color managed and the color must be right,
+ // thus we pick RenderIntent::COLORIMETRIC as render intent for non-HDR
+ // content and pick RenderIntent::TONE_MAP_COLORIMETRIC for HDR content.
+ switch (colorMode) {
+ // In Natural Color Mode, BT2100_PQ and BT2100_HLG will only be picked
+ // when TONE_MAP_COLORIMETRIC is supported.
+ case ColorMode::BT2100_PQ:
+ case ColorMode::BT2100_HLG:
+ return RenderIntent::TONE_MAP_COLORIMETRIC;
default:
- *outMode = ColorMode::SRGB;
- *outDataSpace = Dataspace::V0_SRGB;
- break;
+ return RenderIntent::COLORIMETRIC;
}
}
@@ -2033,8 +2114,9 @@
if (hasWideColorDisplay) {
ColorMode colorMode;
Dataspace dataSpace;
- pickColorMode(displayDevice, &colorMode, &dataSpace);
- setActiveColorModeInternal(displayDevice, colorMode, dataSpace);
+ RenderIntent renderIntent;
+ pickColorMode(displayDevice, &colorMode, &dataSpace, &renderIntent);
+ setActiveColorModeInternal(displayDevice, colorMode, dataSpace, renderIntent);
}
}
@@ -2248,6 +2330,8 @@
const wp<IBinder>& display, int hwcId, const DisplayDeviceState& state,
const sp<DisplaySurface>& dispSurface, const sp<IGraphicBufferProducer>& producer) {
bool hasWideColorGamut = false;
+ std::unordered_map<ColorMode, std::vector<RenderIntent>> hdrAndRenderIntents;
+
if (hasWideColorDisplay) {
std::vector<ColorMode> modes = getHwComposer().getColorModes(hwcId);
for (ColorMode colorMode : modes) {
@@ -2257,7 +2341,6 @@
case ColorMode::DCI_P3:
hasWideColorGamut = true;
break;
- // TODO(lpy) Handle BT2020, BT2100_PQ and BT2100_HLG properly.
default:
break;
}
@@ -2272,6 +2355,10 @@
}
}
}
+
+ if (colorMode == ColorMode::BT2100_PQ || colorMode == ColorMode::BT2100_HLG) {
+ hdrAndRenderIntents.emplace(colorMode, renderIntents);
+ }
}
}
@@ -2310,7 +2397,7 @@
dispSurface, std::move(renderSurface), displayWidth, displayHeight,
hasWideColorGamut, hdrCapabilities,
getHwComposer().getSupportedPerFrameMetadata(hwcId),
- initialPowerMode);
+ hdrAndRenderIntents, initialPowerMode);
if (maxFrameBufferAcquiredBuffers >= 3) {
nativeWindowSurface->preallocateBuffers();
@@ -2322,7 +2409,8 @@
defaultColorMode = ColorMode::SRGB;
defaultDataSpace = Dataspace::V0_SRGB;
}
- setActiveColorModeInternal(hw, defaultColorMode, defaultDataSpace);
+ setActiveColorModeInternal(hw, defaultColorMode, defaultDataSpace,
+ RenderIntent::COLORIMETRIC);
hw->setLayerStack(state.layerStack);
hw->setProjection(state.orientation, state.viewport, state.frame);
hw->setDisplayName(state.displayName);
@@ -2994,6 +3082,8 @@
mat4 savedMatrix;
if (mDisplayColorSetting == DisplayColorSetting::ENHANCED &&
layer->isLegacySrgbDataSpace()) {
+ // TODO(b/78891890) Legacy sRGB saturation matrix should be set
+ // separately.
savedMatrix =
getRenderEngine().setupColorTransform(legacySrgbSaturationMatrix);
restore = true;