surfaceflinger: generalize Description::mIsWideGamut
Generalize the mIsWideGamut bool into input and output transfer
functions. In most cases, the transfer functions are LINEAR,
meaning EOTF() and OETF() are no-op.
Test: manual
Change-Id: I18bfa97ffa950190b553a2ebb18f68b72ab296dd
diff --git a/services/surfaceflinger/RenderEngine/Description.cpp b/services/surfaceflinger/RenderEngine/Description.cpp
index 1b5a466..d70083f 100644
--- a/services/surfaceflinger/RenderEngine/Description.cpp
+++ b/services/surfaceflinger/RenderEngine/Description.cpp
@@ -61,8 +61,12 @@
return mColorMatrix;
}
-void Description::setWideGamut(bool wideGamut) {
- mIsWideGamut = wideGamut;
+void Description::setInputTransferFunction(TransferFunction transferFunction) {
+ mInputTransferFunction = transferFunction;
+}
+
+void Description::setOutputTransferFunction(TransferFunction transferFunction) {
+ mOutputTransferFunction = transferFunction;
}
} /* namespace android */
diff --git a/services/surfaceflinger/RenderEngine/Description.h b/services/surfaceflinger/RenderEngine/Description.h
index 1811952..b0b08fe 100644
--- a/services/surfaceflinger/RenderEngine/Description.h
+++ b/services/surfaceflinger/RenderEngine/Description.h
@@ -32,6 +32,27 @@
* Program and ProgramCache are friends and access the state directly
*/
class Description {
+public:
+ Description() = default;
+ ~Description() = default;
+
+ void setPremultipliedAlpha(bool premultipliedAlpha);
+ void setOpaque(bool opaque);
+ void setTexture(const Texture& texture);
+ void disableTexture();
+ void setColor(const half4& color);
+ void setProjectionMatrix(const mat4& mtx);
+ void setColorMatrix(const mat4& mtx);
+ const mat4& getColorMatrix() const;
+
+ enum class TransferFunction : int {
+ LINEAR,
+ SRGB,
+ };
+ void setInputTransferFunction(TransferFunction transferFunction);
+ void setOutputTransferFunction(TransferFunction transferFunction);
+
+private:
friend class Program;
friend class ProgramCache;
@@ -52,21 +73,9 @@
bool mColorMatrixEnabled = false;
mat4 mColorMatrix;
- bool mIsWideGamut = false;
-
-public:
- Description() = default;
- ~Description() = default;
-
- void setPremultipliedAlpha(bool premultipliedAlpha);
- void setOpaque(bool opaque);
- void setTexture(const Texture& texture);
- void disableTexture();
- void setColor(const half4& color);
- void setProjectionMatrix(const mat4& mtx);
- void setColorMatrix(const mat4& mtx);
- const mat4& getColorMatrix() const;
- void setWideGamut(bool wideGamut);
+ // transfer functions for the input/output
+ TransferFunction mInputTransferFunction = TransferFunction::LINEAR;
+ TransferFunction mOutputTransferFunction = TransferFunction::LINEAR;
};
} /* namespace android */
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
index d1ee6f8..745929d 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
@@ -130,14 +130,8 @@
// Compute sRGB to DisplayP3 color transform
// NOTE: For now, we are limiting wide-color support to
// Display-P3 only.
- mat3 srgbToP3 =
- ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::DisplayP3()).getTransform();
-
- // color transform needs to be expanded to 4x4 to be what the shader wants
- // mat has an initializer that expands mat3 to mat4, but
- // not an assignment operator
- mat4 gamutTransform(srgbToP3);
- mSrgbToDisplayP3 = gamutTransform;
+ mSrgbToDisplayP3 = mat4(
+ ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::DisplayP3()).getTransform());
}
}
@@ -324,10 +318,16 @@
if (usesWideColor()) {
Description wideColorState = mState;
- if (mDataSpace != HAL_DATASPACE_DISPLAY_P3) {
- wideColorState.setColorMatrix(mState.getColorMatrix() * mSrgbToDisplayP3);
- wideColorState.setWideGamut(true);
- ALOGV("drawMesh: gamut transform applied");
+ switch (mDataSpace) {
+ case HAL_DATASPACE_DISPLAY_P3:
+ // input matches output
+ break;
+ default:
+ wideColorState.setColorMatrix(mState.getColorMatrix() * mSrgbToDisplayP3);
+ wideColorState.setInputTransferFunction(Description::TransferFunction::SRGB);
+ wideColorState.setOutputTransferFunction(Description::TransferFunction::SRGB);
+ ALOGV("drawMesh: gamut transform applied");
+ break;
}
ProgramCache::getInstance().useProgram(wideColorState);
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.cpp b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
index 3b8ac0e..30d2369 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.cpp
+++ b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
@@ -126,9 +126,30 @@
.set(Key::OPACITY_MASK,
description.mOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT)
.set(Key::COLOR_MATRIX_MASK,
- description.mColorMatrixEnabled ? Key::COLOR_MATRIX_ON : Key::COLOR_MATRIX_OFF)
- .set(Key::WIDE_GAMUT_MASK,
- description.mIsWideGamut ? Key::WIDE_GAMUT_ON : Key::WIDE_GAMUT_OFF);
+ description.mColorMatrixEnabled ? Key::COLOR_MATRIX_ON : Key::COLOR_MATRIX_OFF);
+
+ if (needs.hasColorMatrix()) {
+ switch (description.mInputTransferFunction) {
+ case Description::TransferFunction::LINEAR:
+ default:
+ needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_LINEAR);
+ break;
+ case Description::TransferFunction::SRGB:
+ needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_SRGB);
+ break;
+ }
+
+ switch (description.mOutputTransferFunction) {
+ case Description::TransferFunction::LINEAR:
+ default:
+ needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_LINEAR);
+ break;
+ case Description::TransferFunction::SRGB:
+ needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_SRGB);
+ break;
+ }
+ }
+
return needs;
}
@@ -172,51 +193,61 @@
if (needs.hasColorMatrix()) {
fs << "uniform mat4 colorMatrix;";
- }
- if (needs.hasColorMatrix()) {
- // When in wide gamut mode, the color matrix will contain a color space
- // conversion matrix that needs to be applied in linear space
- // When not in wide gamut, we can simply no-op the transfer functions
- // and let the shader compiler get rid of them
- if (needs.isWideGamut()) {
- fs << R"__SHADER__(
- float OETF_sRGB(const float linear) {
- return linear <= 0.0031308 ?
- linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
- }
- vec3 OETF_sRGB(const vec3 linear) {
- return vec3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
- }
+ switch (needs.getInputTF()) {
+ case Key::INPUT_TF_LINEAR:
+ default:
+ fs << R"__SHADER__(
+ vec3 EOTF(const vec3 linear) {
+ return linear;
+ }
+ )__SHADER__";
+ break;
+ case Key::INPUT_TF_SRGB:
+ fs << R"__SHADER__(
+ float EOTF_sRGB(float srgb) {
+ return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
+ }
- vec3 OETF_scRGB(const vec3 linear) {
- return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
- }
+ vec3 EOTF_sRGB(const vec3 srgb) {
+ return vec3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+ }
- float EOTF_sRGB(float srgb) {
- return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
- }
+ vec3 EOTF(const vec3 srgb) {
+ return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+ }
+ )__SHADER__";
+ break;
+ }
- vec3 EOTF_sRGB(const vec3 srgb) {
- return vec3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
- }
+ switch (needs.getOutputTF()) {
+ case Key::OUTPUT_TF_LINEAR:
+ default:
+ fs << R"__SHADER__(
+ vec3 OETF(const vec3 linear) {
+ return linear;
+ }
+ )__SHADER__";
+ break;
+ case Key::OUTPUT_TF_SRGB:
+ fs << R"__SHADER__(
+ float OETF_sRGB(const float linear) {
+ return linear <= 0.0031308 ?
+ linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
+ }
- vec3 EOTF_scRGB(const vec3 srgb) {
- return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
- }
- )__SHADER__";
- } else {
- fs << R"__SHADER__(
- vec3 OETF_scRGB(const vec3 linear) {
- return linear;
- }
+ vec3 OETF_sRGB(const vec3 linear) {
+ return vec3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+ }
- vec3 EOTF_scRGB(const vec3 srgb) {
- return srgb;
- }
- )__SHADER__";
+ vec3 OETF(const vec3 linear) {
+ return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+ }
+ )__SHADER__";
+ break;
}
}
+
fs << "void main(void) {" << indent;
if (needs.isTexturing()) {
fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
@@ -243,9 +274,9 @@
// avoid divide by 0 by adding 0.5/256 to the alpha channel
fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);";
}
- fs << "vec4 transformed = colorMatrix * vec4(EOTF_scRGB(gl_FragColor.rgb), 1);";
+ fs << "vec4 transformed = colorMatrix * vec4(EOTF(gl_FragColor.rgb), 1);";
// We assume the last row is always {0,0,0,1} and we skip the division by w
- fs << "gl_FragColor.rgb = OETF_scRGB(transformed.rgb);";
+ fs << "gl_FragColor.rgb = OETF(transformed.rgb);";
if (!needs.isOpaque() && needs.isPremultiplied()) {
// and re-premultiply if needed after gamma correction
fs << "gl_FragColor.rgb = gl_FragColor.rgb * (gl_FragColor.a + 0.0019);";
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.h b/services/surfaceflinger/RenderEngine/ProgramCache.h
index 54d3722..491fad3 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.h
+++ b/services/surfaceflinger/RenderEngine/ProgramCache.h
@@ -50,30 +50,41 @@
public:
enum {
- BLEND_PREMULT = 0x00000001,
- BLEND_NORMAL = 0x00000000,
- BLEND_MASK = 0x00000001,
+ BLEND_SHIFT = 0,
+ BLEND_MASK = 1 << BLEND_SHIFT,
+ BLEND_PREMULT = 1 << BLEND_SHIFT,
+ BLEND_NORMAL = 0 << BLEND_SHIFT,
- OPACITY_OPAQUE = 0x00000002,
- OPACITY_TRANSLUCENT = 0x00000000,
- OPACITY_MASK = 0x00000002,
+ OPACITY_SHIFT = 1,
+ OPACITY_MASK = 1 << OPACITY_SHIFT,
+ OPACITY_OPAQUE = 1 << OPACITY_SHIFT,
+ OPACITY_TRANSLUCENT = 0 << OPACITY_SHIFT,
- ALPHA_LT_ONE = 0x00000004,
- ALPHA_EQ_ONE = 0x00000000,
- ALPHA_MASK = 0x00000004,
+ ALPHA_SHIFT = 2,
+ ALPHA_MASK = 1 << ALPHA_SHIFT,
+ ALPHA_LT_ONE = 1 << ALPHA_SHIFT,
+ ALPHA_EQ_ONE = 0 << ALPHA_SHIFT,
- TEXTURE_OFF = 0x00000000,
- TEXTURE_EXT = 0x00000008,
- TEXTURE_2D = 0x00000010,
- TEXTURE_MASK = 0x00000018,
+ TEXTURE_SHIFT = 3,
+ TEXTURE_MASK = 3 << TEXTURE_SHIFT,
+ TEXTURE_OFF = 0 << TEXTURE_SHIFT,
+ TEXTURE_EXT = 1 << TEXTURE_SHIFT,
+ TEXTURE_2D = 2 << TEXTURE_SHIFT,
- COLOR_MATRIX_OFF = 0x00000000,
- COLOR_MATRIX_ON = 0x00000020,
- COLOR_MATRIX_MASK = 0x00000020,
+ COLOR_MATRIX_SHIFT = 5,
+ COLOR_MATRIX_MASK = 1 << COLOR_MATRIX_SHIFT,
+ COLOR_MATRIX_OFF = 0 << COLOR_MATRIX_SHIFT,
+ COLOR_MATRIX_ON = 1 << COLOR_MATRIX_SHIFT,
- WIDE_GAMUT_OFF = 0x00000000,
- WIDE_GAMUT_ON = 0x00000040,
- WIDE_GAMUT_MASK = 0x00000040,
+ INPUT_TF_SHIFT = 6,
+ INPUT_TF_MASK = 3 << INPUT_TF_SHIFT,
+ INPUT_TF_LINEAR = 0 << INPUT_TF_SHIFT,
+ INPUT_TF_SRGB = 1 << INPUT_TF_SHIFT,
+
+ OUTPUT_TF_SHIFT = 8,
+ OUTPUT_TF_MASK = 3 << OUTPUT_TF_SHIFT,
+ OUTPUT_TF_LINEAR = 0 << OUTPUT_TF_SHIFT,
+ OUTPUT_TF_SRGB = 1 << OUTPUT_TF_SHIFT,
};
inline Key() : mKey(0) {}
@@ -90,7 +101,8 @@
inline bool isOpaque() const { return (mKey & OPACITY_MASK) == OPACITY_OPAQUE; }
inline bool hasAlpha() const { return (mKey & ALPHA_MASK) == ALPHA_LT_ONE; }
inline bool hasColorMatrix() const { return (mKey & COLOR_MATRIX_MASK) == COLOR_MATRIX_ON; }
- inline bool isWideGamut() const { return (mKey & WIDE_GAMUT_MASK) == WIDE_GAMUT_ON; }
+ inline int getInputTF() const { return (mKey & INPUT_TF_MASK); }
+ inline int getOutputTF() const { return (mKey & OUTPUT_TF_MASK); }
// this is the definition of a friend function -- not a method of class Needs
friend inline int strictly_order_type(const Key& lhs, const Key& rhs) {