summaryrefslogtreecommitdiff
path: root/libs/hwui/ProgramCache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/hwui/ProgramCache.cpp')
-rw-r--r--libs/hwui/ProgramCache.cpp137
1 files changed, 118 insertions, 19 deletions
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 38c23e4babe8..1f78e09b5a58 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -161,17 +161,61 @@ const char* gFS_Uniforms_HasRoundRectClip =
"uniform vec4 roundRectInnerRectLTRB;\n"
"uniform float roundRectRadius;\n";
+const char* gFS_Uniforms_ColorSpaceConversion =
+ // TODO: Should we use a 3D LUT to combine the matrix and transfer functions?
+ // 32x32x32 fp16 LUTs (for scRGB output) are large and heavy to generate...
+ "uniform mat3 colorSpaceMatrix;\n";
+
+const char* gFS_Uniforms_TransferFunction[4] = {
+ // In this order: g, a, b, c, d, e, f
+ // See ColorSpace::TransferParameters
+ // We'll use hardware sRGB conversion as much as possible
+ "",
+ "uniform float transferFunction[7];\n",
+ "uniform float transferFunction[5];\n",
+ "uniform float transferFunctionGamma;\n"
+};
+
const char* gFS_OETF[2] = {
- "\nvec4 OETF(const vec4 linear) {\n"
- " return linear;\n"
- "}\n",
- // We expect linear data to be scRGB so we mirror the gamma function
- "\nvec4 OETF(const vec4 linear) {"
- " return vec4(sign(linear.rgb) * OETF_sRGB(abs(linear.rgb)), linear.a);\n"
- "}\n",
+ R"__SHADER__(
+ vec4 OETF(const vec4 linear) {
+ return linear;
+ }
+ )__SHADER__",
+ // We expect linear data to be scRGB so we mirror the gamma function
+ R"__SHADER__(
+ vec4 OETF(const vec4 linear) {
+ return vec4(sign(linear.rgb) * OETF_sRGB(abs(linear.rgb)), linear.a);
+ }
+ )__SHADER__"
+};
+
+const char* gFS_ColorConvert[3] = {
+ // Just OETF
+ R"__SHADER__(
+ vec4 colorConvert(const vec4 color) {
+ return OETF(color);
+ }
+ )__SHADER__",
+ // Full color conversion for opaque bitmaps
+ R"__SHADER__(
+ vec4 colorConvert(const vec4 color) {
+ return OETF(vec4(colorSpaceMatrix * EOTF_Parametric(color.rgb), color.a));
+ }
+ )__SHADER__",
+ // Full color conversion for translucent bitmaps
+ // Note: 0.5/256=0.0019
+ R"__SHADER__(
+ vec4 colorConvert(in vec4 color) {
+ color.rgb /= color.a + 0.0019;
+ color = OETF(vec4(colorSpaceMatrix * EOTF_Parametric(color.rgb), color.a));
+ color.rgb *= color.a + 0.0019;
+ return color;
+ }
+ )__SHADER__",
};
-const char* gFS_Transfer_Functions = R"__SHADER__(
+const char* gFS_sRGB_TransferFunctions = R"__SHADER__(
float OETF_sRGB(const float linear) {
// IEC 61966-2-1:1999
return linear <= 0.0031308 ? linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
@@ -187,12 +231,56 @@ const char* gFS_Transfer_Functions = R"__SHADER__(
}
)__SHADER__";
+const char* gFS_TransferFunction[4] = {
+ // Conversion done by the texture unit (sRGB)
+ R"__SHADER__(
+ vec3 EOTF_Parametric(const vec3 x) {
+ return x;
+ }
+ )__SHADER__",
+ // Full transfer function
+ // TODO: We should probably use a 1D LUT (256x1 with texelFetch() since input is 8 bit)
+ // TODO: That would cause 3 dependent texture fetches. Is it worth it?
+ R"__SHADER__(
+ float EOTF_Parametric(float x) {
+ return x <= transferFunction[4]
+ ? transferFunction[3] * x + transferFunction[6]
+ : pow(transferFunction[1] * x + transferFunction[2], transferFunction[0])
+ + transferFunction[5];
+ }
+
+ vec3 EOTF_Parametric(const vec3 x) {
+ return vec3(EOTF_Parametric(x.r), EOTF_Parametric(x.g), EOTF_Parametric(x.b));
+ }
+ )__SHADER__",
+ // Limited transfer function, e = f = 0.0
+ R"__SHADER__(
+ float EOTF_Parametric(float x) {
+ return x <= transferFunction[4]
+ ? transferFunction[3] * x
+ : pow(transferFunction[1] * x + transferFunction[2], transferFunction[0]);
+ }
+
+ vec3 EOTF_Parametric(const vec3 x) {
+ return vec3(EOTF_Parametric(x.r), EOTF_Parametric(x.g), EOTF_Parametric(x.b));
+ }
+ )__SHADER__",
+ // Gamma transfer function, e = f = 0.0
+ R"__SHADER__(
+ vec3 EOTF_Parametric(const vec3 x) {
+ return vec3(pow(x.r, transferFunctionGamma),
+ pow(x.g, transferFunctionGamma),
+ pow(x.b, transferFunctionGamma));
+ }
+ )__SHADER__"
+};
+
// Dithering must be done in the quantization space
// When we are writing to an sRGB framebuffer, we must do the following:
// EOTF(OETF(color) + dither)
// The dithering pattern is generated with a triangle noise generator in the range [-1.0,1.0]
// TODO: Handle linear fp16 render targets
-const char* gFS_Gradient_Functions = R"__SHADER__(
+const char* gFS_GradientFunctions = R"__SHADER__(
float triangleNoise(const highp vec2 n) {
highp vec2 p = fract(n * vec2(5.3987, 5.4421));
p += dot(p.yx, p.xy + vec2(21.5351, 14.3137));
@@ -200,7 +288,8 @@ const char* gFS_Gradient_Functions = R"__SHADER__(
return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;
}
)__SHADER__";
-const char* gFS_Gradient_Preamble[2] = {
+
+const char* gFS_GradientPreamble[2] = {
// Linear framebuffer
R"__SHADER__(
vec4 dither(const vec4 color) {
@@ -259,9 +348,9 @@ const char* gFS_Main_ApplyVertexAlphaShadowInterp =
" fragColor *= texture2D(baseSampler, vec2(alpha, 0.5)).a;\n";
const char* gFS_Main_FetchTexture[2] = {
// Don't modulate
- " fragColor = OETF(texture2D(baseSampler, outTexCoords));\n",
+ " fragColor = colorConvert(texture2D(baseSampler, outTexCoords));\n",
// Modulate
- " fragColor = color * texture2D(baseSampler, outTexCoords);\n"
+ " fragColor = color * colorConvert(texture2D(baseSampler, outTexCoords));\n"
};
const char* gFS_Main_FetchA8Texture[4] = {
// Don't modulate
@@ -290,9 +379,9 @@ const char* gFS_Main_FetchGradient[6] = {
" vec4 gradientColor = gradientMix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n"
};
const char* gFS_Main_FetchBitmap =
- " vec4 bitmapColor = OETF(texture2D(bitmapSampler, outBitmapTexCoords));\n";
+ " vec4 bitmapColor = colorConvert(texture2D(bitmapSampler, outBitmapTexCoords));\n";
const char* gFS_Main_FetchBitmapNpot =
- " vec4 bitmapColor = OETF(texture2D(bitmapSampler, wrap(outBitmapTexCoords)));\n";
+ " vec4 bitmapColor = colorConvert(texture2D(bitmapSampler, wrap(outBitmapTexCoords)));\n";
const char* gFS_Main_BlendShadersBG =
" fragColor = blendShaders(gradientColor, bitmapColor)";
const char* gFS_Main_BlendShadersGB =
@@ -627,6 +716,11 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
}
shader.append(gFS_Uniforms_ColorOp[static_cast<int>(description.colorOp)]);
+ if (description.hasColorSpaceConversion) {
+ shader.append(gFS_Uniforms_ColorSpaceConversion);
+ }
+ shader.append(gFS_Uniforms_TransferFunction[static_cast<int>(description.transferFunction)]);
+
// Generate required functions
if (description.hasGradient && description.hasBitmap) {
generateBlend(shader, "blendShaders", description.shadersMode);
@@ -640,16 +734,21 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
if (description.useShaderBasedWrap) {
generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT);
}
- if (description.hasGradient || description.hasLinearTexture) {
- shader.append(gFS_Transfer_Functions);
+ if (description.hasGradient || description.hasLinearTexture
+ || description.hasColorSpaceConversion) {
+ shader.append(gFS_sRGB_TransferFunctions);
}
if (description.hasBitmap || ((description.hasTexture || description.hasExternalTexture) &&
!description.hasAlpha8Texture)) {
- shader.append(gFS_OETF[description.hasLinearTexture && !mHasLinearBlending]);
+ shader.append(gFS_TransferFunction[static_cast<int>(description.transferFunction)]);
+ shader.append(gFS_OETF[(description.hasLinearTexture || description.hasColorSpaceConversion)
+ && !mHasLinearBlending]);
+ shader.append(gFS_ColorConvert[description.hasColorSpaceConversion
+ ? 1 + description.hasTranslucentConversion : 0]);
}
if (description.hasGradient) {
- shader.append(gFS_Gradient_Functions);
- shader.append(gFS_Gradient_Preamble[mHasLinearBlending]);
+ shader.append(gFS_GradientFunctions);
+ shader.append(gFS_GradientPreamble[mHasLinearBlending]);
}
// Begin the shader