SF/HWC2: Add support for color transforms
Adds support for color transforms using the setColorTransform method
of HWC2.
This means that instead of always falling back to client composition
when applying a transform, SurfaceFlinger will allow the device to
make that decision. If all layers fall back to client composition, the
SKIP_CLIENT_COLOR_TRANSFORM capability allows the device greater
control over whether SF should apply the transform or whether it
should allow the device to apply it to the client target buffer.
Bug: 19539930
Change-Id: I47a3d5453a3c47a8dd105ab77cce7f9c9687e925
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index ae6ba98..87a0e9a 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -317,9 +317,12 @@
"Capability size has changed");
uint32_t numCapabilities = 0;
mHwcDevice->getCapabilities(mHwcDevice, &numCapabilities, nullptr);
- mCapabilities.resize(numCapabilities);
- auto asInt = reinterpret_cast<int32_t*>(mCapabilities.data());
+ std::vector<Capability> capabilities(numCapabilities);
+ auto asInt = reinterpret_cast<int32_t*>(capabilities.data());
mHwcDevice->getCapabilities(mHwcDevice, &numCapabilities, asInt);
+ for (auto capability : capabilities) {
+ mCapabilities.emplace(capability);
+ }
}
bool Device::hasCapability(HWC2::Capability capability) const
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 68781da..beb7bc6 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -33,6 +33,7 @@
#include <functional>
#include <string>
#include <unordered_map>
+#include <unordered_set>
#include <vector>
namespace android {
@@ -66,7 +67,7 @@
std::string dump() const;
- const std::vector<Capability>& getCapabilities() const {
+ const std::unordered_set<Capability>& getCapabilities() const {
return mCapabilities;
};
@@ -181,7 +182,7 @@
HWC2_PFN_SET_LAYER_VISIBLE_REGION mSetLayerVisibleRegion;
HWC2_PFN_SET_LAYER_Z_ORDER mSetLayerZOrder;
- std::vector<Capability> mCapabilities;
+ std::unordered_set<Capability> mCapabilities;
std::unordered_map<hwc2_display_t, std::weak_ptr<Display>> mDisplays;
HotplugCallback mHotplug;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 133e5f1..199848f 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -144,6 +144,11 @@
mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount();
}
+bool HWComposer::hasCapability(HWC2::Capability capability) const
+{
+ return mHwcDevice->getCapabilities().count(capability) > 0;
+}
+
bool HWComposer::isValidDisplay(int32_t displayId) const {
return static_cast<size_t>(displayId) < mDisplayData.size() &&
mDisplayData[displayId].hwcDisplay;
@@ -692,6 +697,28 @@
return NO_ERROR;
}
+status_t HWComposer::setColorTransform(int32_t displayId,
+ const mat4& transform) {
+ if (!isValidDisplay(displayId)) {
+ ALOGE("setColorTransform: Display %d is not valid", displayId);
+ return BAD_INDEX;
+ }
+
+ auto& displayData = mDisplayData[displayId];
+ bool isIdentity = transform == mat4();
+ auto error = displayData.hwcDisplay->setColorTransform(transform,
+ isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY :
+ HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX);
+ if (error != HWC2::Error::None) {
+ ALOGE("setColorTransform: Failed to set transform on display %d: "
+ "%s (%d)", displayId, to_string(error).c_str(),
+ static_cast<int32_t>(error));
+ return UNKNOWN_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
void HWComposer::disconnectDisplay(int displayId) {
LOG_ALWAYS_FATAL_IF(displayId < 0);
auto& displayData = mDisplayData[displayId];
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index aa233df..17676ae 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -81,6 +81,8 @@
void setEventHandler(EventHandler* handler);
+ bool hasCapability(HWC2::Capability capability) const;
+
// Attempts to allocate a virtual display. If the virtual display is created
// on the HWC device, outId will contain its HWC ID.
status_t allocateVirtualDisplay(uint32_t width, uint32_t height,
@@ -104,6 +106,9 @@
// set active config
status_t setActiveConfig(int32_t displayId, size_t configId);
+ // Sets a color transform to be applied to the result of composition
+ status_t setColorTransform(int32_t displayId, const mat4& transform);
+
// reset state when an external, non-virtual display is disconnected
void disconnectDisplay(int32_t displayId);
diff --git a/services/surfaceflinger/Effects/Daltonizer.cpp b/services/surfaceflinger/Effects/Daltonizer.cpp
index feb8936..a104e8f 100644
--- a/services/surfaceflinger/Effects/Daltonizer.cpp
+++ b/services/surfaceflinger/Effects/Daltonizer.cpp
@@ -19,21 +19,14 @@
namespace android {
-Daltonizer::Daltonizer() :
- mType(deuteranomaly), mMode(simulation), mDirty(true) {
-}
-
-Daltonizer::~Daltonizer() {
-}
-
-void Daltonizer::setType(Daltonizer::ColorBlindnessTypes type) {
+void Daltonizer::setType(ColorBlindnessType type) {
if (type != mType) {
mDirty = true;
mType = type;
}
}
-void Daltonizer::setMode(Daltonizer::Mode mode) {
+void Daltonizer::setMode(ColorBlindnessMode mode) {
if (mode != mMode) {
mDirty = true;
mMode = mode;
@@ -49,6 +42,11 @@
}
void Daltonizer::update() {
+ if (mType == ColorBlindnessType::None) {
+ mColorTransform = mat4();
+ return;
+ }
+
// converts a linear RGB color to the XYZ space
const mat4 rgb2xyz( 0.4124, 0.2126, 0.0193, 0,
0.3576, 0.7152, 0.1192, 0,
@@ -149,24 +147,25 @@
mat4 correction(0);
switch (mType) {
- case protanopia:
- case protanomaly:
+ case ColorBlindnessType::Protanomaly:
simulation = lms2lmsp;
- if (mMode == Daltonizer::correction)
+ if (mMode == ColorBlindnessMode::Correction)
correction = errp;
break;
- case deuteranopia:
- case deuteranomaly:
+ case ColorBlindnessType::Deuteranomaly:
simulation = lms2lmsd;
- if (mMode == Daltonizer::correction)
+ if (mMode == ColorBlindnessMode::Correction)
correction = errd;
break;
- case tritanopia:
- case tritanomaly:
+ case ColorBlindnessType::Tritanomaly:
simulation = lms2lmst;
- if (mMode == Daltonizer::correction)
+ if (mMode == ColorBlindnessMode::Correction)
correction = errt;
break;
+ case ColorBlindnessType::None:
+ // We already caught this at the beginning of the method, but the
+ // compiler doesn't know that
+ break;
}
mColorTransform = lms2rgb *
diff --git a/services/surfaceflinger/Effects/Daltonizer.h b/services/surfaceflinger/Effects/Daltonizer.h
index e816437..d21b155 100644
--- a/services/surfaceflinger/Effects/Daltonizer.h
+++ b/services/surfaceflinger/Effects/Daltonizer.h
@@ -21,27 +21,22 @@
namespace android {
+enum class ColorBlindnessType {
+ None, // Disables the Daltonizer
+ Protanomaly, // L (red) cone deficient
+ Deuteranomaly, // M (green) cone deficient (most common)
+ Tritanomaly // S (blue) cone deficient
+};
+
+enum class ColorBlindnessMode {
+ Simulation,
+ Correction
+};
+
class Daltonizer {
public:
- enum ColorBlindnessTypes {
- protanopia, // L (red) cone missing
- deuteranopia, // M (green) cone missing
- tritanopia, // S (blue) cone missing
- protanomaly, // L (red) cone deficient
- deuteranomaly, // M (green) cone deficient (most common)
- tritanomaly // S (blue) cone deficient
- };
-
- enum Mode {
- simulation,
- correction
- };
-
- Daltonizer();
- ~Daltonizer();
-
- void setType(ColorBlindnessTypes type);
- void setMode(Mode mode);
+ void setType(ColorBlindnessType type);
+ void setMode(ColorBlindnessMode mode);
// returns the color transform to apply in the shader
const mat4& operator()();
@@ -49,9 +44,9 @@
private:
void update();
- ColorBlindnessTypes mType;
- Mode mMode;
- bool mDirty;
+ ColorBlindnessType mType = ColorBlindnessType::None;
+ ColorBlindnessMode mMode = ColorBlindnessMode::Simulation;
+ bool mDirty = true;
mat4 mColorTransform;
};
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5c7db2b..f298399 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -152,7 +152,6 @@
mPrimaryDispSync("PrimaryDispSync"),
mPrimaryHWVsyncEnabled(false),
mHWVsyncAvailable(false),
- mDaltonize(false),
mHasColorMatrix(false),
mHasPoweredOff(false),
mFrameBuckets(),
@@ -1224,8 +1223,7 @@
}
layer->setGeometry(displayDevice);
- if (mDebugDisableHWC || mDebugRegion || mDaltonize ||
- mHasColorMatrix) {
+ if (mDebugDisableHWC || mDebugRegion) {
layer->forceClientComposition(hwcId);
}
}
@@ -1233,6 +1231,9 @@
}
}
+
+ mat4 colorMatrix = mColorMatrix * mDaltonizer();
+
// Set the per-frame data
for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
auto& displayDevice = mDisplays[displayId];
@@ -1240,11 +1241,18 @@
if (hwcId < 0) {
continue;
}
+ if (colorMatrix != mPreviousColorMatrix) {
+ status_t result = mHwc->setColorTransform(hwcId, colorMatrix);
+ ALOGE_IF(result != NO_ERROR, "Failed to set color transform on "
+ "display %zd: %d", displayId, result);
+ }
for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
layer->setPerFrameData(displayDevice);
}
}
+ mPreviousColorMatrix = colorMatrix;
+
for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
auto& displayDevice = mDisplays[displayId];
if (!displayDevice->isDisplayOn()) {
@@ -1926,18 +1934,7 @@
}
}
- if (CC_LIKELY(!mDaltonize && !mHasColorMatrix)) {
- if (!doComposeSurfaces(hw, dirtyRegion)) return;
- } else {
- RenderEngine& engine(getRenderEngine());
- mat4 colorMatrix = mColorMatrix;
- if (mDaltonize) {
- colorMatrix = colorMatrix * mDaltonizer();
- }
- mat4 oldMatrix = engine.setupColorTransform(colorMatrix);
- doComposeSurfaces(hw, dirtyRegion);
- engine.setupColorTransform(oldMatrix);
- }
+ if (!doComposeSurfaces(hw, dirtyRegion)) return;
// update the swap region and clear the dirty region
hw->swapRegion.orSelf(dirtyRegion);
@@ -1952,6 +1949,15 @@
ALOGV("doComposeSurfaces");
const auto hwcId = displayDevice->getHwcDisplayId();
+
+ mat4 oldColorMatrix;
+ const bool applyColorMatrix = !mHwc->hasDeviceComposition(hwcId) &&
+ !mHwc->hasCapability(HWC2::Capability::SkipClientColorTransform);
+ if (applyColorMatrix) {
+ mat4 colorMatrix = mColorMatrix * mDaltonizer();
+ oldColorMatrix = getRenderEngine().setupColorTransform(colorMatrix);
+ }
+
bool hasClientComposition = mHwc->hasClientComposition(hwcId);
if (hasClientComposition) {
ALOGV("hasClientComposition");
@@ -2069,6 +2075,10 @@
}
}
+ if (applyColorMatrix) {
+ getRenderEngine().setupColorTransform(oldColorMatrix);
+ }
+
// disable scissor at the end of the frame
mRenderEngine->disableScissor();
return true;
@@ -2944,8 +2954,7 @@
colorizer.bold(result);
result.append("h/w composer state:\n");
colorizer.reset(result);
- bool hwcDisabled = mDebugDisableHWC || mDebugRegion || mDaltonize ||
- mHasColorMatrix;
+ bool hwcDisabled = mDebugDisableHWC || mDebugRegion;
result.appendFormat(" h/w composer %s\n",
hwcDisabled ? "disabled" : "enabled");
hwc.dump(result);
@@ -3100,16 +3109,24 @@
// daltonize
n = data.readInt32();
switch (n % 10) {
- case 1: mDaltonizer.setType(Daltonizer::protanomaly); break;
- case 2: mDaltonizer.setType(Daltonizer::deuteranomaly); break;
- case 3: mDaltonizer.setType(Daltonizer::tritanomaly); break;
+ case 1:
+ mDaltonizer.setType(ColorBlindnessType::Protanomaly);
+ break;
+ case 2:
+ mDaltonizer.setType(ColorBlindnessType::Deuteranomaly);
+ break;
+ case 3:
+ mDaltonizer.setType(ColorBlindnessType::Tritanomaly);
+ break;
+ default:
+ mDaltonizer.setType(ColorBlindnessType::None);
+ break;
}
if (n >= 10) {
- mDaltonizer.setMode(Daltonizer::correction);
+ mDaltonizer.setMode(ColorBlindnessMode::Correction);
} else {
- mDaltonizer.setMode(Daltonizer::simulation);
+ mDaltonizer.setMode(ColorBlindnessMode::Simulation);
}
- mDaltonize = n > 0;
invalidateHwcGeometry();
repaintEverything();
return NO_ERROR;
@@ -3117,15 +3134,14 @@
case 1015: {
// apply a color matrix
n = data.readInt32();
- mHasColorMatrix = n ? 1 : 0;
if (n) {
// color matrix is sent as mat3 matrix followed by vec3
// offset, then packed into a mat4 where the last row is
// the offset and extra values are 0
for (size_t i = 0 ; i < 4; i++) {
- for (size_t j = 0; j < 4; j++) {
- mColorMatrix[i][j] = data.readFloat();
- }
+ for (size_t j = 0; j < 4; j++) {
+ mColorMatrix[i][j] = data.readFloat();
+ }
}
} else {
mColorMatrix = mat4();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 0df39a4..f063aaf 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -527,8 +527,11 @@
*/
Daltonizer mDaltonizer;
+#ifndef USE_HWC2
bool mDaltonize;
+#endif
+ mat4 mPreviousColorMatrix;
mat4 mColorMatrix;
bool mHasColorMatrix;
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index 69fb8c5..7062ea2 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -3080,14 +3080,20 @@
// daltonize
n = data.readInt32();
switch (n % 10) {
- case 1: mDaltonizer.setType(Daltonizer::protanomaly); break;
- case 2: mDaltonizer.setType(Daltonizer::deuteranomaly); break;
- case 3: mDaltonizer.setType(Daltonizer::tritanomaly); break;
+ case 1:
+ mDaltonizer.setType(ColorBlindnessType::Protanomaly);
+ break;
+ case 2:
+ mDaltonizer.setType(ColorBlindnessType::Deuteranomaly);
+ break;
+ case 3:
+ mDaltonizer.setType(ColorBlindnessType::Tritanomaly);
+ break;
}
if (n >= 10) {
- mDaltonizer.setMode(Daltonizer::correction);
+ mDaltonizer.setMode(ColorBlindnessMode::Correction);
} else {
- mDaltonizer.setMode(Daltonizer::simulation);
+ mDaltonizer.setMode(ColorBlindnessMode::Simulation);
}
mDaltonize = n > 0;
invalidateHwcGeometry();