[SurfaceFinger] Adds auto color mode support in SurfaceFlinger.
This patch adds auto color mode support in SurfaceFlinger. For Auto Awesome
Color milestone 1, we expect the hardware composer is capable of handling P3
layers, meaning if the hardware composer is given P3 layers, it will strech the
color from Display P3 to the pannel native color space. Hardware composer may
punt sRGB layers back to SurfaceFlinger, in this case, we fall back to
RenderEngine. We will set the destination data space to Display P3 when the
layers are mixed color spaces, and set the destination data space as SRGB if
they are all SRGB layers.
BUG: 73824924
BUG: 73825729
Test: Build
Change-Id: I577841b14de0cfe8c29f8aa30bee8621c5d72976
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 58a774b..d90ab1d 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -55,6 +55,7 @@
using namespace android::hardware::configstore;
using namespace android::hardware::configstore::V1_0;
using android::ui::ColorMode;
+using android::ui::RenderIntent;
/*
* Initialize the display to the specified values.
@@ -75,8 +76,8 @@
std::unique_ptr<RE::Surface> renderSurface,
int displayWidth,
int displayHeight,
- bool supportWideColor,
- bool supportHdr,
+ bool hasWideColorGamut,
+ bool hasHdr10,
int initialPowerMode)
: lastCompositionHadVisibleLayers(false),
mFlinger(flinger),
@@ -98,8 +99,8 @@
mActiveConfig(0),
mActiveColorMode(ColorMode::NATIVE),
mColorTransform(HAL_COLOR_TRANSFORM_IDENTITY),
- mDisplayHasWideColor(supportWideColor),
- mDisplayHasHdr(supportHdr)
+ mHasWideColorGamut(hasWideColorGamut),
+ mHasHdr10(hasHdr10)
{
// clang-format on
@@ -268,6 +269,14 @@
return mActiveColorMode;
}
+RenderIntent DisplayDevice::getActiveRenderIntent() const {
+ return mActiveRenderIntent;
+}
+
+void DisplayDevice::setActiveRenderIntent(RenderIntent renderIntent) {
+ mActiveRenderIntent = renderIntent;
+}
+
void DisplayDevice::setColorTransform(const mat4& transform) {
const bool isIdentity = (transform == mat4());
mColorTransform =
@@ -279,10 +288,15 @@
}
void DisplayDevice::setCompositionDataSpace(ui::Dataspace dataspace) {
+ mCompositionDataSpace = dataspace;
ANativeWindow* const window = mNativeWindow.get();
native_window_set_buffers_data_space(window, static_cast<android_dataspace>(dataspace));
}
+ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
+ return mCompositionDataSpace;
+}
+
// ----------------------------------------------------------------------------
void DisplayDevice::setLayerStack(uint32_t stack) {
@@ -464,8 +478,8 @@
tr[0][1], tr[1][1], tr[2][1], tr[0][2], tr[1][2], tr[2][2]);
auto const surface = static_cast<Surface*>(window);
ui::Dataspace dataspace = surface->getBuffersDataSpace();
- result.appendFormat(" wideColor=%d, hdr=%d, colorMode=%s, dataspace: %s (%d)\n",
- mDisplayHasWideColor, mDisplayHasHdr,
+ result.appendFormat(" wideColorGamut=%d, hdr10=%d, colorMode=%s, dataspace: %s (%d)\n",
+ mHasWideColorGamut, mHasHdr10,
decodeColorMode(mActiveColorMode).c_str(),
dataspaceDetails(static_cast<android_dataspace>(dataspace)).c_str(), dataspace);
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index e844d11..bedf765 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -84,8 +84,8 @@
std::unique_ptr<RE::Surface> renderSurface,
int displayWidth,
int displayHeight,
- bool supportWideColor,
- bool supportHdr,
+ bool hasWideColorGamut,
+ bool hasHdr10,
int initialPowerMode);
// clang-format on
@@ -135,8 +135,8 @@
// machine happy without actually queueing a buffer if nothing has changed
status_t beginFrame(bool mustRecompose) const;
status_t prepareFrame(HWComposer& hwc);
- bool getWideColorSupport() const { return mDisplayHasWideColor; }
- bool getHdrSupport() const { return mDisplayHasHdr; }
+ bool hasWideColorGamut() const { return mHasWideColorGamut; }
+ bool hasHdr10() const { return mHasHdr10; }
void swapBuffers(HWComposer& hwc) const;
@@ -165,9 +165,12 @@
ui::ColorMode getActiveColorMode() const;
void setActiveColorMode(ui::ColorMode mode);
+ ui::RenderIntent getActiveRenderIntent() const;
+ void setActiveRenderIntent(ui::RenderIntent renderIntent);
android_color_transform_t getColorTransform() const;
void setColorTransform(const mat4& transform);
void setCompositionDataSpace(ui::Dataspace dataspace);
+ ui::Dataspace getCompositionDataSpace() const;
/* ------------------------------------------------------------------------
* Display active config management.
@@ -241,14 +244,17 @@
int mActiveConfig;
// current active color mode
ui::ColorMode mActiveColorMode;
+ // Current active render intent.
+ ui::RenderIntent mActiveRenderIntent;
+ ui::Dataspace mCompositionDataSpace;
// Current color transform
android_color_transform_t mColorTransform;
// Need to know if display is wide-color capable or not.
// Initialized by SurfaceFlinger when the DisplayDevice is created.
// Fed to RenderEngine during composition.
- bool mDisplayHasWideColor;
- bool mDisplayHasHdr;
+ bool mHasWideColorGamut;
+ bool mHasHdr10;
};
struct DisplayDeviceState {
@@ -290,9 +296,9 @@
bool isSecure() const override { return mDevice->isSecure(); }
bool needsFiltering() const override { return mDevice->needsFiltering(); }
Rect getSourceCrop() const override { return mSourceCrop; }
- bool getWideColorSupport() const override { return mDevice->getWideColorSupport(); }
- ui::ColorMode getActiveColorMode() const override {
- return mDevice->getActiveColorMode();
+ bool getWideColorSupport() const override { return mDevice->hasWideColorGamut(); }
+ ui::Dataspace getDataSpace() const override {
+ return mDevice->getCompositionDataSpace();
}
private:
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 9d356d8..8c0050e 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1643,6 +1643,21 @@
return true;
}
+// Dataspace::UNKNOWN, Dataspace::SRGB, Dataspace::SRGB_LINEAR,
+// Dataspace::V0_SRGB and Dataspace::V0_SRGB_LINEAR are considered legacy
+// SRGB data space for now.
+// Note that Dataspace::V0_SRGB and Dataspace::V0_SRGB_LINEAR are not legacy
+// data space, however since framework doesn't distinguish them out of legacy
+// SRGB, we have to treat them as the same for now.
+bool Layer::isLegacySrgbDataSpace() const {
+ // TODO(lpy) b/77652630, need to figure out when UNKNOWN can be treated as SRGB.
+ return mDrawingState.dataSpace == ui::Dataspace::UNKNOWN ||
+ mDrawingState.dataSpace == ui::Dataspace::SRGB ||
+ mDrawingState.dataSpace == ui::Dataspace::SRGB_LINEAR ||
+ mDrawingState.dataSpace == ui::Dataspace::V0_SRGB ||
+ mDrawingState.dataSpace == ui::Dataspace::V0_SRGB_LINEAR;
+}
+
void Layer::setParent(const sp<Layer>& layer) {
mCurrentParent = layer;
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 8d2a048..d382a1a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -297,6 +297,13 @@
bool reparent(const sp<IBinder>& newParentHandle);
bool detachChildren();
+ // Before color management is introduced, contents on Android have to be
+ // desaturated in order to match what they appears like visually.
+ // With color management, these contents will appear desaturated, thus
+ // needed to be saturated so that they match what they are designed for
+ // visually. When returns true, legacy SRGB data space is passed to HWC.
+ bool isLegacySrgbDataSpace() const;
+
// If we have received a new buffer this frame, we will pass its surface
// damage down to hardware composer. Otherwise, we must send a region with
// one empty rect.
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index bf0707f..4f812fc 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -25,15 +25,14 @@
virtual bool isSecure() const = 0;
virtual bool needsFiltering() const = 0;
virtual Rect getSourceCrop() const = 0;
+ virtual bool getWideColorSupport() const = 0;
+ virtual ui::Dataspace getDataSpace() const = 0;
virtual void render(std::function<void()> drawLayers) { drawLayers(); }
int getReqHeight() const { return mReqHeight; };
int getReqWidth() const { return mReqWidth; };
Transform::orientation_flags getRotationFlags() const { return mRotationFlags; };
- virtual bool getWideColorSupport() const = 0;
- virtual ui::ColorMode getActiveColorMode() const = 0;
-
status_t updateDimensions();
private:
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 183c1eb..abd3a9e 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -157,6 +157,31 @@
return std::string(value) == "true";
}
+DisplayColorSetting toDisplayColorSetting(int value) {
+ switch(value) {
+ case 0:
+ return DisplayColorSetting::MANAGED;
+ case 1:
+ return DisplayColorSetting::UNMANAGED;
+ case 2:
+ return DisplayColorSetting::ENHANCED;
+ default:
+ return DisplayColorSetting::MANAGED;
+ }
+}
+
+std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) {
+ switch(displayColorSetting) {
+ case DisplayColorSetting::MANAGED:
+ return std::string("Natural Mode");
+ case DisplayColorSetting::UNMANAGED:
+ return std::string("Saturated Mode");
+ case DisplayColorSetting::ENHANCED:
+ return std::string("Auto Color Mode");
+ }
+ return std::string("Unknown Display Color Setting");
+}
+
NativeWindowSurface::~NativeWindowSurface() = default;
namespace impl {
@@ -690,6 +715,9 @@
ALOGE("Run StartPropertySetThread failed!");
}
+ mLegacySrgbSaturationMatrix = getBE().mHwc->getDataspaceSaturationMatrix(HWC_DISPLAY_PRIMARY,
+ Dataspace::SRGB_LINEAR);
+
ALOGV("Done initializing");
}
@@ -697,14 +725,13 @@
char value[PROPERTY_VALUE_MAX];
property_get("persist.sys.sf.color_saturation", value, "1.0");
- mSaturation = atof(value);
- ALOGV("Saturation is set to %.2f", mSaturation);
+ mGlobalSaturationFactor = atof(value);
+ ALOGV("Saturation is set to %.2f", mGlobalSaturationFactor);
property_get("persist.sys.sf.native_mode", value, "0");
- mForceNativeColorMode = atoi(value) == 1;
- if (mForceNativeColorMode) {
- ALOGV("Forcing native color mode");
- }
+ mDisplayColorSetting = toDisplayColorSetting(atoi(value));
+ ALOGV("Display Color Setting is set to %s.",
+ decodeDisplayColorSetting(mDisplayColorSetting).c_str());
}
void SurfaceFlinger::startBootAnim() {
@@ -989,11 +1016,12 @@
}
void SurfaceFlinger::setActiveColorModeInternal(const sp<DisplayDevice>& hw,
- ColorMode mode) {
+ ColorMode mode, Dataspace dataSpace) {
int32_t type = hw->getDisplayType();
ColorMode currentMode = hw->getActiveColorMode();
+ Dataspace currentDataSpace = hw->getCompositionDataSpace();
- if (mode == currentMode) {
+ if (mode == currentMode && dataSpace == currentDataSpace) {
return;
}
@@ -1002,11 +1030,29 @@
return;
}
- ALOGD("Set active color mode: %s (%d), type=%d", decodeColorMode(mode).c_str(), mode,
- hw->getDisplayType());
-
hw->setActiveColorMode(mode);
- getHwComposer().setActiveColorMode(type, mode, RenderIntent::COLORIMETRIC);
+ hw->setCompositionDataSpace(dataSpace);
+ // 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->getDisplayType() == DisplayDevice::DISPLAY_PRIMARY) {
+ renderIntent = RenderIntent::ENHANCE;
+ }
+ hw->setActiveRenderIntent(renderIntent);
+ getHwComposer().setActiveColorMode(type, mode, renderIntent);
+
+ ALOGV("Set active color mode: %s (%d), active render intent: %s (%d), type=%d",
+ decodeColorMode(mode).c_str(), mode,
+ decodeRenderIntent(renderIntent).c_str(), renderIntent,
+ hw->getDisplayType());
}
@@ -1037,7 +1083,7 @@
ALOGW("Attempt to set active color mode %s %d for virtual display",
decodeColorMode(mMode).c_str(), mMode);
} else {
- mFlinger.setActiveColorModeInternal(hw, mMode);
+ mFlinger.setActiveColorModeInternal(hw, mMode, Dataspace::UNKNOWN);
}
return true;
}
@@ -1072,7 +1118,7 @@
std::unique_ptr<HdrCapabilities> capabilities =
getBE().mHwc->getHdrCapabilities(displayDevice->getHwcDisplayId());
if (capabilities) {
- if (displayDevice->getWideColorSupport() && !displayDevice->getHdrSupport()) {
+ if (displayDevice->hasWideColorGamut() && !displayDevice->hasHdr10()) {
// insert HDR10 as we will force client composition for HDR10
// layers
std::vector<int32_t> types = capabilities->getSupportedHdrTypes();
@@ -1821,73 +1867,78 @@
}
mat4 SurfaceFlinger::computeSaturationMatrix() const {
- if (mSaturation == 1.0f) {
+ if (mGlobalSaturationFactor == 1.0f) {
return mat4();
}
// Rec.709 luma coefficients
float3 luminance{0.213f, 0.715f, 0.072f};
- luminance *= 1.0f - mSaturation;
+ luminance *= 1.0f - mGlobalSaturationFactor;
return mat4(
- vec4{luminance.r + mSaturation, luminance.r, luminance.r, 0.0f},
- vec4{luminance.g, luminance.g + mSaturation, luminance.g, 0.0f},
- vec4{luminance.b, luminance.b, luminance.b + mSaturation, 0.0f},
+ vec4{luminance.r + mGlobalSaturationFactor, luminance.r, luminance.r, 0.0f},
+ vec4{luminance.g, luminance.g + mGlobalSaturationFactor, luminance.g, 0.0f},
+ vec4{luminance.b, luminance.b, luminance.b + mGlobalSaturationFactor, 0.0f},
vec4{0.0f, 0.0f, 0.0f, 1.0f}
);
}
-// pickColorMode translates a given dataspace into the best available color mode.
-// Currently only support sRGB and Display-P3.
-ColorMode SurfaceFlinger::pickColorMode(Dataspace dataSpace) const {
- if (mForceNativeColorMode) {
- return ColorMode::NATIVE;
+// Returns a dataspace that fits all visible layers. The returned dataspace
+// 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;
+ 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::BT2020_PQ:
+ case Dataspace::BT2020_ITU_PQ:
+ // Historically, HDR dataspaces are ignored by SurfaceFlinger. But
+ // since SurfaceFlinger simulates HDR support now, it should honor
+ // them unless there is also native support.
+ if (!displayDevice->hasHdr10()) {
+ return Dataspace::V0_SCRGB_LINEAR;
+ }
+ break;
+ case Dataspace::DISPLAY_P3:
+ bestDataspace = Dataspace::DISPLAY_P3;
+ break;
+ default:
+ break;
+ }
}
- switch (dataSpace) {
- // treat Unknown as regular SRGB buffer, since that's what the rest of the
- // system expects.
- case Dataspace::UNKNOWN:
- case Dataspace::SRGB:
- case Dataspace::V0_SRGB:
- return ColorMode::SRGB;
- break;
-
- case Dataspace::DISPLAY_P3:
- return ColorMode::DISPLAY_P3;
- break;
-
- default:
- // TODO (courtneygo): Do we want to assert an error here?
- ALOGE("No color mode mapping for %s (%#x)",
- dataspaceDetails(static_cast<android_dataspace>(dataSpace)).c_str(),
- dataSpace);
- return ColorMode::SRGB;
- break;
- }
+ return bestDataspace;
}
-Dataspace SurfaceFlinger::bestTargetDataSpace(
- Dataspace a, Dataspace b, bool hasHdr) const {
- // Only support sRGB and Display-P3 right now.
- if (a == Dataspace::DISPLAY_P3 || b == Dataspace::DISPLAY_P3) {
- return Dataspace::DISPLAY_P3;
- }
- if (a == Dataspace::V0_SCRGB_LINEAR || b == Dataspace::V0_SCRGB_LINEAR) {
- return Dataspace::DISPLAY_P3;
- }
- if (a == Dataspace::V0_SCRGB || b == Dataspace::V0_SCRGB) {
- return Dataspace::DISPLAY_P3;
- }
- if (!hasHdr) {
- if (a == Dataspace::BT2020_PQ || b == Dataspace::BT2020_PQ) {
- return Dataspace::DISPLAY_P3;
- }
- if (a == Dataspace::BT2020_ITU_PQ || b == Dataspace::BT2020_ITU_PQ) {
- return Dataspace::DISPLAY_P3;
- }
+// 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 {
+ if (mDisplayColorSetting == DisplayColorSetting::UNMANAGED) {
+ *outMode = ColorMode::NATIVE;
+ *outDataSpace = Dataspace::UNKNOWN;
+ return;
}
- return Dataspace::V0_SRGB;
+ switch (getBestDataspace(displayDevice)) {
+ 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;
+ }
}
void SurfaceFlinger::setUpHWComposer() {
@@ -1970,7 +2021,7 @@
for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
if ((layer->getDataSpace() == Dataspace::BT2020_PQ ||
layer->getDataSpace() == Dataspace::BT2020_ITU_PQ) &&
- !displayDevice->getHdrSupport()) {
+ !displayDevice->hasHdr10()) {
layer->forceClientComposition(hwcId);
}
@@ -1984,19 +2035,10 @@
}
if (hasWideColorDisplay) {
- ColorMode newColorMode;
- Dataspace newDataSpace = Dataspace::V0_SRGB;
-
- for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
- newDataSpace = bestTargetDataSpace(layer->getDataSpace(), newDataSpace,
- displayDevice->getHdrSupport());
- ALOGV("layer: %s, dataspace: %s (%#x), newDataSpace: %s (%#x)",
- layer->getName().string(), dataspaceDetails(static_cast<android_dataspace>(layer->getDataSpace())).c_str(),
- layer->getDataSpace(), dataspaceDetails(static_cast<android_dataspace>(newDataSpace)).c_str(), newDataSpace);
- }
- newColorMode = pickColorMode(newDataSpace);
-
- setActiveColorModeInternal(displayDevice, newColorMode);
+ ColorMode colorMode;
+ Dataspace dataSpace;
+ pickColorMode(displayDevice, &colorMode, &dataSpace);
+ setActiveColorModeInternal(displayDevice, colorMode, dataSpace);
}
}
@@ -2204,29 +2246,40 @@
sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
const wp<IBinder>& display, int hwcId, const DisplayDeviceState& state,
const sp<DisplaySurface>& dispSurface, const sp<IGraphicBufferProducer>& producer) {
- bool hasWideColorSupport = false;
+ bool hasWideColorGamut = false;
if (hasWideColorDisplay) {
- std::vector<ColorMode> modes = getHwComposer().getColorModes(state.type);
+ std::vector<ColorMode> modes = getHwComposer().getColorModes(hwcId);
for (ColorMode colorMode : modes) {
switch (colorMode) {
case ColorMode::DISPLAY_P3:
case ColorMode::ADOBE_RGB:
case ColorMode::DCI_P3:
- hasWideColorSupport = true;
+ hasWideColorGamut = true;
break;
+ // TODO(lpy) Handle BT2020, BT2100_PQ and BT2100_HLG properly.
default:
break;
}
+
+ std::vector<RenderIntent> renderIntents = getHwComposer().getRenderIntents(hwcId,
+ colorMode);
+ if (state.type == DisplayDevice::DISPLAY_PRIMARY) {
+ for (auto intent : renderIntents) {
+ if (intent == RenderIntent::ENHANCE) {
+ mBuiltinDisplaySupportsEnhance = true;
+ break;
+ }
+ }
+ }
}
}
- bool hasHdrSupport = false;
- std::unique_ptr<HdrCapabilities> hdrCapabilities =
- getHwComposer().getHdrCapabilities(state.type);
+ bool hasHdr10 = false;
+ std::unique_ptr<HdrCapabilities> hdrCapabilities = getHwComposer().getHdrCapabilities(hwcId);
if (hdrCapabilities) {
const std::vector<int32_t> types = hdrCapabilities->getSupportedHdrTypes();
auto iter = std::find(types.cbegin(), types.cend(), HAL_HDR_HDR10);
- hasHdrSupport = iter != types.cend();
+ hasHdr10 = iter != types.cend();
}
auto nativeWindowSurface = mCreateNativeWindowSurface(producer);
@@ -2260,18 +2313,19 @@
sp<DisplayDevice> hw =
new DisplayDevice(this, state.type, hwcId, state.isSecure, display, nativeWindow,
dispSurface, std::move(renderSurface), displayWidth, displayHeight,
- hasWideColorSupport, hasHdrSupport, initialPowerMode);
+ hasWideColorGamut, hasHdr10, initialPowerMode);
if (maxFrameBufferAcquiredBuffers >= 3) {
nativeWindowSurface->preallocateBuffers();
}
ColorMode defaultColorMode = ColorMode::NATIVE;
- if (hasWideColorSupport) {
+ Dataspace defaultDataSpace = Dataspace::UNKNOWN;
+ if (hasWideColorGamut) {
defaultColorMode = ColorMode::SRGB;
+ defaultDataSpace = Dataspace::V0_SRGB;
}
- setActiveColorModeInternal(hw, defaultColorMode);
- hw->setCompositionDataSpace(Dataspace::UNKNOWN);
+ setActiveColorModeInternal(hw, defaultColorMode, defaultDataSpace);
hw->setLayerStack(state.layerStack);
hw->setProjection(state.orientation, state.viewport, state.frame);
hw->setDisplayName(state.displayName);
@@ -2822,23 +2876,27 @@
const Region bounds(displayDevice->bounds());
const DisplayRenderArea renderArea(displayDevice);
const auto hwcId = displayDevice->getHwcDisplayId();
+ const bool hasClientComposition = getBE().mHwc->hasClientComposition(hwcId);
+ const bool hasDeviceComposition = getBE().mHwc->hasDeviceComposition(hwcId);
+ const bool skipClientColorTransform = getBE().mHwc->hasCapability(
+ HWC2::Capability::SkipClientColorTransform);
+ ATRACE_INT("hasClientComposition", hasClientComposition);
mat4 oldColorMatrix;
- const bool applyColorMatrix = !getBE().mHwc->hasDeviceComposition(hwcId) &&
- !getBE().mHwc->hasCapability(HWC2::Capability::SkipClientColorTransform);
+ mat4 legacySrgbSaturationMatrix = mLegacySrgbSaturationMatrix;
+ const bool applyColorMatrix = !hasDeviceComposition && !skipClientColorTransform;
if (applyColorMatrix) {
- mat4 colorMatrix = mColorMatrix * mDaltonizer();
+ mat4 colorMatrix = mColorMatrix * computeSaturationMatrix() * mDaltonizer();
oldColorMatrix = getRenderEngine().setupColorTransform(colorMatrix);
+ legacySrgbSaturationMatrix = colorMatrix * legacySrgbSaturationMatrix;
}
- bool hasClientComposition = getBE().mHwc->hasClientComposition(hwcId);
if (hasClientComposition) {
ALOGV("hasClientComposition");
Dataspace outputDataspace = Dataspace::UNKNOWN;
- if (displayDevice->getWideColorSupport() &&
- displayDevice->getActiveColorMode() == ColorMode::DISPLAY_P3) {
- outputDataspace = Dataspace::DISPLAY_P3;
+ if (displayDevice->hasWideColorGamut()) {
+ outputDataspace = displayDevice->getCompositionDataSpace();
}
getBE().mRenderEngine->setOutputDataSpace(outputDataspace);
@@ -2855,7 +2913,6 @@
}
// Never touch the framebuffer if we don't have any framebuffer layers
- const bool hasDeviceComposition = getBE().mHwc->hasDeviceComposition(hwcId);
if (hasDeviceComposition) {
// when using overlays, we assume a fully transparent framebuffer
// NOTE: we could reduce how much we need to clear, for instance
@@ -2930,7 +2987,20 @@
break;
}
case HWC2::Composition::Client: {
+ // Only apply saturation matrix layer that is legacy SRGB dataspace
+ // when auto color mode is on.
+ bool restore = false;
+ mat4 savedMatrix;
+ if (mDisplayColorSetting == DisplayColorSetting::ENHANCED &&
+ layer->isLegacySrgbDataSpace()) {
+ savedMatrix =
+ getRenderEngine().setupColorTransform(legacySrgbSaturationMatrix);
+ restore = true;
+ }
layer->draw(renderArea, clip);
+ if (restore) {
+ getRenderEngine().setupColorTransform(savedMatrix);
+ }
break;
}
default:
@@ -4004,7 +4074,7 @@
void SurfaceFlinger::dumpWideColorInfo(String8& result) const {
result.appendFormat("hasWideColorDisplay: %d\n", hasWideColorDisplay);
- result.appendFormat("forceNativeColorMode: %d\n", mForceNativeColorMode);
+ result.appendFormat("DisplayColorSetting: %d\n", mDisplayColorSetting);
// TODO: print out if wide-color mode is active or not
@@ -4500,15 +4570,22 @@
return NO_ERROR;
}
case 1022: { // Set saturation boost
- mSaturation = std::max(0.0f, std::min(data.readFloat(), 2.0f));
+ mGlobalSaturationFactor = std::max(0.0f, std::min(data.readFloat(), 2.0f));
invalidateHwcGeometry();
repaintEverything();
return NO_ERROR;
}
case 1023: { // Set native mode
- mForceNativeColorMode = data.readInt32() == 1;
+ int32_t value = data.readInt32();
+ if (value > 2) {
+ return BAD_VALUE;
+ }
+ if (value == 2 && !mBuiltinDisplaySupportsEnhance) {
+ return BAD_VALUE;
+ }
+ mDisplayColorSetting = toDisplayColorSetting(value);
invalidateHwcGeometry();
repaintEverything();
return NO_ERROR;
@@ -4535,6 +4612,23 @@
reply->writeBool(mTracing.isEnabled());
return NO_ERROR;
}
+ // Is a DisplayColorSetting supported?
+ case 1027: {
+ int32_t value = data.readInt32();
+ switch (value) {
+ case 0:
+ reply->writeBool(hasWideColorDisplay);
+ return NO_ERROR;
+ case 1:
+ reply->writeBool(true);
+ return NO_ERROR;
+ case 2:
+ reply->writeBool(mBuiltinDisplaySupportsEnhance);
+ return NO_ERROR;
+ default:
+ return BAD_VALUE;
+ }
+ }
}
}
return err;
@@ -4602,6 +4696,7 @@
mCrop(crop),
mFlinger(flinger),
mChildrenOnly(childrenOnly) {}
+ const Transform& getTransform() const override { return mTransform; }
Rect getBounds() const override {
const Layer::State& layerState(mLayer->getDrawingState());
return Rect(layerState.active.w, layerState.active.h);
@@ -4610,7 +4705,15 @@
int getWidth() const override { return mLayer->getDrawingState().active.w; }
bool isSecure() const override { return false; }
bool needsFiltering() const override { return false; }
- const Transform& getTransform() const { return mTransform; }
+ Rect getSourceCrop() const override {
+ if (mCrop.isEmpty()) {
+ return getBounds();
+ } else {
+ return mCrop;
+ }
+ }
+ bool getWideColorSupport() const override { return false; }
+ Dataspace getDataSpace() const override { return Dataspace::UNKNOWN; }
class ReparentForDrawing {
public:
@@ -4639,16 +4742,6 @@
}
}
- Rect getSourceCrop() const override {
- if (mCrop.isEmpty()) {
- return getBounds();
- } else {
- return mCrop;
- }
- }
- bool getWideColorSupport() const override { return false; }
- ColorMode getActiveColorMode() const override { return ColorMode::NATIVE; }
-
private:
const sp<Layer> mLayer;
const Rect mCrop;
@@ -4820,9 +4913,8 @@
}
Dataspace outputDataspace = Dataspace::UNKNOWN;
- if (renderArea.getWideColorSupport() &&
- renderArea.getActiveColorMode() == ColorMode::DISPLAY_P3) {
- outputDataspace = Dataspace::DISPLAY_P3;
+ if (renderArea.getWideColorSupport()) {
+ outputDataspace = renderArea.getDataSpace();
}
getBE().mRenderEngine->setOutputDataSpace(outputDataspace);
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index a29d1d7..14028ff 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -123,6 +123,12 @@
eTransactionMask = 0x0f,
};
+enum class DisplayColorSetting : int32_t {
+ MANAGED = 0,
+ UNMANAGED = 1,
+ ENHANCED = 2,
+};
+
// A thin interface to abstract creating instances of Surface (gui/Surface.h) to
// use as a NativeWindow.
class NativeWindowSurface {
@@ -467,7 +473,9 @@
bool stateLockHeld);
// Called on the main thread in response to setActiveColorMode()
- void setActiveColorModeInternal(const sp<DisplayDevice>& hw, ui::ColorMode colorMode);
+ void setActiveColorModeInternal(const sp<DisplayDevice>& hw,
+ ui::ColorMode colorMode,
+ ui::Dataspace dataSpace);
// Returns whether the transaction actually modified any state
bool handleMessageTransaction();
@@ -641,9 +649,10 @@
// Given a dataSpace, returns the appropriate color_mode to use
// to display that dataSpace.
- ui::ColorMode pickColorMode(ui::Dataspace dataSpace) const;
- ui::Dataspace bestTargetDataSpace(ui::Dataspace a, ui::Dataspace b,
- bool hasHdr) const;
+ ui::Dataspace getBestDataspace(const sp<const DisplayDevice>& displayDevice) const;
+ void pickColorMode(const sp<DisplayDevice>& displayDevice,
+ ui::ColorMode* outMode,
+ ui::Dataspace* outDataSpace) const;
mat4 computeSaturationMatrix() const;
@@ -846,7 +855,6 @@
size_t mNumLayers;
-
// Verify that transaction is being called by an approved process:
// either AID_GRAPHICS or AID_SYSTEM.
status_t CheckTransactCodeCredentials(uint32_t code);
@@ -856,8 +864,12 @@
static bool useVrFlinger;
std::thread::id mMainThreadId;
- float mSaturation = 1.0f;
- bool mForceNativeColorMode = false;
+ DisplayColorSetting mDisplayColorSetting = DisplayColorSetting::MANAGED;
+ // Applied on sRGB layers when the render intent is non-colorimetric.
+ mat4 mLegacySrgbSaturationMatrix;
+ // Applied globally.
+ float mGlobalSaturationFactor = 1.0f;
+ bool mBuiltinDisplaySupportsEnhance = false;
using CreateBufferQueueFunction =
std::function<void(sp<IGraphicBufferProducer>* /* outProducer */,