diff options
Diffstat (limited to 'libs/hwui/ProgramCache.cpp')
-rw-r--r-- | libs/hwui/ProgramCache.cpp | 302 |
1 files changed, 151 insertions, 151 deletions
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index 59225e108ac7..ca056487a3ba 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -17,8 +17,8 @@ #include <utils/String8.h> #include "Caches.h" -#include "Dither.h" #include "ProgramCache.h" +#include "Properties.h" namespace android { namespace uirenderer { @@ -69,22 +69,16 @@ const char* gVS_Header_Varyings_HasBitmap = "varying highp vec2 outBitmapTexCoords;\n"; const char* gVS_Header_Varyings_HasGradient[6] = { // Linear - "varying highp vec2 linear;\n" - "varying vec2 ditherTexCoords;\n", - "varying float linear;\n" - "varying vec2 ditherTexCoords;\n", + "varying highp vec2 linear;\n", + "varying float linear;\n", // Circular - "varying highp vec2 circular;\n" - "varying vec2 ditherTexCoords;\n", - "varying highp vec2 circular;\n" - "varying vec2 ditherTexCoords;\n", + "varying highp vec2 circular;\n", + "varying highp vec2 circular;\n", // Sweep - "varying highp vec2 sweep;\n" - "varying vec2 ditherTexCoords;\n", - "varying highp vec2 sweep;\n" - "varying vec2 ditherTexCoords;\n", + "varying highp vec2 sweep;\n", + "varying highp vec2 sweep;\n", }; const char* gVS_Header_Varyings_HasRoundRectClip = "varying highp vec2 roundRectPos;\n"; @@ -98,22 +92,16 @@ const char* gVS_Main_OutTransformedTexCoords = " outTexCoords = (mainTextureTransform * vec4(texCoords, 0.0, 1.0)).xy;\n"; const char* gVS_Main_OutGradient[6] = { // Linear - " linear = vec2((screenSpace * position).x, 0.5);\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", - " linear = (screenSpace * position).x;\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", + " linear = vec2((screenSpace * position).x, 0.5);\n", + " linear = (screenSpace * position).x;\n", // Circular - " circular = (screenSpace * position).xy;\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", - " circular = (screenSpace * position).xy;\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", + " circular = (screenSpace * position).xy;\n", + " circular = (screenSpace * position).xy;\n", // Sweep + " sweep = (screenSpace * position).xy;\n", " sweep = (screenSpace * position).xy;\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", - " sweep = (screenSpace * position).xy;\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", }; const char* gVS_Main_OutBitmapTexCoords = " outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n"; @@ -147,17 +135,18 @@ const char* gFS_Uniforms_TextureSampler = "uniform sampler2D baseSampler;\n"; const char* gFS_Uniforms_ExternalTextureSampler = "uniform samplerExternalOES baseSampler;\n"; -const char* gFS_Uniforms_Dither = - "uniform sampler2D ditherSampler;"; const char* gFS_Uniforms_GradientSampler[2] = { - "%s\n" + "uniform vec2 screenSize;\n" "uniform sampler2D gradientSampler;\n", - "%s\n" + + "uniform vec2 screenSize;\n" "uniform vec4 startColor;\n" "uniform vec4 endColor;\n" }; const char* gFS_Uniforms_BitmapSampler = "uniform sampler2D bitmapSampler;\n"; +const char* gFS_Uniforms_BitmapExternalSampler = + "uniform samplerExternalOES bitmapSampler;\n"; const char* gFS_Uniforms_ColorOp[3] = { // None "", @@ -172,56 +161,88 @@ const char* gFS_Uniforms_HasRoundRectClip = "uniform vec4 roundRectInnerRectLTRB;\n" "uniform float roundRectRadius;\n"; -const char* gFS_Main = - "\nvoid main(void) {\n" - " lowp vec4 fragColor;\n"; - -const char* gFS_Main_Dither[2] = { - // ES 2.0 - "texture2D(ditherSampler, ditherTexCoords).a * " STR(DITHER_KERNEL_SIZE_INV_SQUARE), - // ES 3.0 - "texture2D(ditherSampler, ditherTexCoords).a" +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", }; -const char* gFS_Main_AddDitherToGradient = - " gradientColor += %s;\n"; -// Fast cases -const char* gFS_Fast_SingleColor = - "\nvoid main(void) {\n" - " gl_FragColor = color;\n" - "}\n\n"; -const char* gFS_Fast_SingleTexture = - "\nvoid main(void) {\n" - " gl_FragColor = texture2D(baseSampler, outTexCoords);\n" - "}\n\n"; -const char* gFS_Fast_SingleModulateTexture = - "\nvoid main(void) {\n" - " gl_FragColor = color.a * texture2D(baseSampler, outTexCoords);\n" - "}\n\n"; -const char* gFS_Fast_SingleA8Texture = - "\nvoid main(void) {\n" - " gl_FragColor = texture2D(baseSampler, outTexCoords);\n" - "}\n\n"; -const char* gFS_Fast_SingleModulateA8Texture = - "\nvoid main(void) {\n" - " gl_FragColor = color * texture2D(baseSampler, outTexCoords).a;\n" - "}\n\n"; -const char* gFS_Fast_SingleGradient[2] = { - "\nvoid main(void) {\n" - " gl_FragColor = %s + texture2D(gradientSampler, linear);\n" - "}\n\n", - "\nvoid main(void) {\n" - " gl_FragColor = %s + mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n" - "}\n\n", +const char* gFS_Transfer_Functions = 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; + } + + vec3 OETF_sRGB(const vec3 linear) { + return vec3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b)); + } + + float EOTF_sRGB(float srgb) { + // IEC 61966-2-1:1999 + return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4); + } +)__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 [-0.0,1.0] +// TODO: Handle linear fp16 render targets +const char* gFS_Gradient_Functions = 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)); + highp float xy = p.x * p.y; + return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0; + } +)__SHADER__"; +const char* gFS_Gradient_Preamble[2] = { + // Linear framebuffer + "\nvec4 dither(const vec4 color) {\n" + " return vec4(color.rgb + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0), color.a);\n" + "}\n" + "\nvec4 gammaMix(const vec4 a, const vec4 b, float v) {\n" + " vec4 c = mix(a, b, v);\n" + " c.a = EOTF_sRGB(c.a);\n" // This is technically incorrect but preserves compatibility + " return vec4(OETF_sRGB(c.rgb) * c.a, c.a);\n" + "}\n", + // sRGB framebuffer + "\nvec4 dither(const vec4 color) {\n" + " vec3 dithered = sqrt(color.rgb) + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0);\n" + " return vec4(dithered * dithered, color.a);\n" + "}\n" + "\nvec4 gammaMix(const vec4 a, const vec4 b, float v) {\n" + " vec4 c = mix(a, b, v);\n" + " return vec4(c.rgb * c.a, c.a);\n" + "}\n" }; -const char* gFS_Fast_SingleModulateGradient[2] = { - "\nvoid main(void) {\n" - " gl_FragColor = %s + color.a * texture2D(gradientSampler, linear);\n" - "}\n\n", + +// Uses luminance coefficients from Rec.709 to choose the appropriate gamma +// The gamma() function assumes that bright text will be displayed on a dark +// background and that dark text will be displayed on bright background +// The gamma coefficient is chosen to thicken or thin the text accordingly +// The dot product used to compute the luminance could be approximated with +// a simple max(color.r, color.g, color.b) +const char* gFS_Gamma_Preamble = R"__SHADER__( + #define GAMMA (%.2f) + #define GAMMA_INV (%.2f) + + float gamma(float a, const vec3 color) { + float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722)); + return pow(a, luminance < 0.5 ? GAMMA_INV : GAMMA); + } +)__SHADER__"; + +const char* gFS_Main = "\nvoid main(void) {\n" - " gl_FragColor = %s + color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n" - "}\n\n" -}; + " vec4 fragColor;\n"; + +const char* gFS_Main_AddDither = + " fragColor = dither(fragColor);\n"; // General case const char* gFS_Main_FetchColor = @@ -235,65 +256,76 @@ const char* gFS_Main_ApplyVertexAlphaShadowInterp = " fragColor *= texture2D(baseSampler, vec2(alpha, 0.5)).a;\n"; const char* gFS_Main_FetchTexture[2] = { // Don't modulate - " fragColor = texture2D(baseSampler, outTexCoords);\n", + " fragColor = OETF(texture2D(baseSampler, outTexCoords));\n", // Modulate " fragColor = color * texture2D(baseSampler, outTexCoords);\n" }; -const char* gFS_Main_FetchA8Texture[2] = { +const char* gFS_Main_FetchA8Texture[4] = { // Don't modulate " fragColor = texture2D(baseSampler, outTexCoords);\n", + " fragColor = texture2D(baseSampler, outTexCoords);\n", // Modulate " fragColor = color * texture2D(baseSampler, outTexCoords).a;\n", + " fragColor = color * gamma(texture2D(baseSampler, outTexCoords).a, color.rgb);\n", }; const char* gFS_Main_FetchGradient[6] = { // Linear " vec4 gradientColor = texture2D(gradientSampler, linear);\n", - " vec4 gradientColor = mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n", + " vec4 gradientColor = gammaMix(startColor, endColor, clamp(linear, 0.0, 1.0));\n", // Circular " vec4 gradientColor = texture2D(gradientSampler, vec2(length(circular), 0.5));\n", - " vec4 gradientColor = mix(startColor, endColor, clamp(length(circular), 0.0, 1.0));\n", + " vec4 gradientColor = gammaMix(startColor, endColor, clamp(length(circular), 0.0, 1.0));\n", // Sweep " highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n" " vec4 gradientColor = texture2D(gradientSampler, vec2(index - floor(index), 0.5));\n", " highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n" - " vec4 gradientColor = mix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n" + " vec4 gradientColor = gammaMix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n" }; const char* gFS_Main_FetchBitmap = - " vec4 bitmapColor = texture2D(bitmapSampler, outBitmapTexCoords);\n"; + " vec4 bitmapColor = OETF(texture2D(bitmapSampler, outBitmapTexCoords));\n"; const char* gFS_Main_FetchBitmapNpot = - " vec4 bitmapColor = texture2D(bitmapSampler, wrap(outBitmapTexCoords));\n"; + " vec4 bitmapColor = OETF(texture2D(bitmapSampler, wrap(outBitmapTexCoords)));\n"; const char* gFS_Main_BlendShadersBG = " fragColor = blendShaders(gradientColor, bitmapColor)"; const char* gFS_Main_BlendShadersGB = " fragColor = blendShaders(bitmapColor, gradientColor)"; -const char* gFS_Main_BlendShaders_Modulate[3] = { +const char* gFS_Main_BlendShaders_Modulate[6] = { // Don't modulate ";\n", + ";\n", // Modulate " * color.a;\n", + " * color.a;\n", // Modulate with alpha 8 texture " * texture2D(baseSampler, outTexCoords).a;\n", + " * gamma(texture2D(baseSampler, outTexCoords).a, color.rgb);\n", }; -const char* gFS_Main_GradientShader_Modulate[3] = { +const char* gFS_Main_GradientShader_Modulate[6] = { // Don't modulate " fragColor = gradientColor;\n", + " fragColor = gradientColor;\n", // Modulate " fragColor = gradientColor * color.a;\n", + " fragColor = gradientColor * color.a;\n", // Modulate with alpha 8 texture " fragColor = gradientColor * texture2D(baseSampler, outTexCoords).a;\n", + " fragColor = gradientColor * gamma(texture2D(baseSampler, outTexCoords).a, gradientColor.rgb);\n", }; -const char* gFS_Main_BitmapShader_Modulate[3] = { +const char* gFS_Main_BitmapShader_Modulate[6] = { // Don't modulate " fragColor = bitmapColor;\n", + " fragColor = bitmapColor;\n", // Modulate " fragColor = bitmapColor * color.a;\n", + " fragColor = bitmapColor * color.a;\n", // Modulate with alpha 8 texture " fragColor = bitmapColor * texture2D(baseSampler, outTexCoords).a;\n", + " fragColor = bitmapColor * gamma(texture2D(baseSampler, outTexCoords).a, bitmapColor.rgb);\n", }; const char* gFS_Main_FragColor = " gl_FragColor = fragColor;\n"; @@ -385,7 +417,8 @@ const char* gBlendOps[18] = { /////////////////////////////////////////////////////////////////////////////// ProgramCache::ProgramCache(Extensions& extensions) - : mHasES3(extensions.getMajorGlVersion() >= 3) { + : mHasES3(extensions.getMajorGlVersion() >= 3) + , mHasSRGB(extensions.hasSRGB()) { } ProgramCache::~ProgramCache() { @@ -518,6 +551,7 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description static bool shaderOp(const ProgramDescription& description, String8& shader, const int modulateOp, const char** snippets) { int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp; + op = op * 2 + description.hasGammaCorrection; shader.append(snippets[op]); return description.hasAlpha8Texture; } @@ -525,11 +559,12 @@ static bool shaderOp(const ProgramDescription& description, String8& shader, String8 ProgramCache::generateFragmentShader(const ProgramDescription& description) { String8 shader(gFS_Header_Start); - const bool blendFramebuffer = description.framebufferMode >= SkXfermode::kPlus_Mode; + const bool blendFramebuffer = description.framebufferMode >= SkBlendMode::kPlus; if (blendFramebuffer) { shader.append(gFS_Header_Extension_FramebufferFetch); } - if (description.hasExternalTexture) { + if (description.hasExternalTexture + || (description.hasBitmap && description.isShaderBitmapExternal)) { shader.append(gFS_Header_Extension_ExternalTexture); } @@ -570,72 +605,22 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti shader.append(gFS_Uniforms_ExternalTextureSampler); } if (description.hasGradient) { - shader.appendFormat(gFS_Uniforms_GradientSampler[description.isSimpleGradient], - gFS_Uniforms_Dither); + shader.append(gFS_Uniforms_GradientSampler[description.isSimpleGradient]); } if (description.hasRoundRectClip) { shader.append(gFS_Uniforms_HasRoundRectClip); } - // Optimization for common cases - if (!description.hasVertexAlpha - && !blendFramebuffer - && !description.hasColors - && description.colorOp == ProgramDescription::ColorFilterMode::None - && !description.hasDebugHighlight - && !description.hasRoundRectClip) { - bool fast = false; - - const bool noShader = !description.hasGradient && !description.hasBitmap; - const bool singleTexture = (description.hasTexture || description.hasExternalTexture) && - !description.hasAlpha8Texture && noShader; - const bool singleA8Texture = description.hasTexture && - description.hasAlpha8Texture && noShader; - const bool singleGradient = !description.hasTexture && !description.hasExternalTexture && - description.hasGradient && !description.hasBitmap && - description.gradientType == ProgramDescription::kGradientLinear; - - if (singleColor) { - shader.append(gFS_Fast_SingleColor); - fast = true; - } else if (singleTexture) { - if (!description.modulate) { - shader.append(gFS_Fast_SingleTexture); - } else { - shader.append(gFS_Fast_SingleModulateTexture); - } - fast = true; - } else if (singleA8Texture) { - if (!description.modulate) { - shader.append(gFS_Fast_SingleA8Texture); - } else { - shader.append(gFS_Fast_SingleModulateA8Texture); - } - fast = true; - } else if (singleGradient) { - if (!description.modulate) { - shader.appendFormat(gFS_Fast_SingleGradient[description.isSimpleGradient], - gFS_Main_Dither[mHasES3]); - } else { - shader.appendFormat(gFS_Fast_SingleModulateGradient[description.isSimpleGradient], - gFS_Main_Dither[mHasES3]); - } - fast = true; - } - - if (fast) { -#if DEBUG_PROGRAMS - PROGRAM_LOGD("*** Fast case:\n"); - PROGRAM_LOGD("*** Generated fragment shader:\n\n"); - printLongString(shader); -#endif - - return shader; - } + if (description.hasGammaCorrection) { + shader.appendFormat(gFS_Gamma_Preamble, Properties::textGamma, 1.0f / Properties::textGamma); } if (description.hasBitmap) { - shader.append(gFS_Uniforms_BitmapSampler); + if (description.isShaderBitmapExternal) { + shader.append(gFS_Uniforms_BitmapExternalSampler); + } else { + shader.append(gFS_Uniforms_BitmapSampler); + } } shader.append(gFS_Uniforms_ColorOp[static_cast<int>(description.colorOp)]); @@ -649,9 +634,20 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (blendFramebuffer) { generateBlend(shader, "blendFramebuffer", description.framebufferMode); } - if (description.isBitmapNpot) { + if (description.useShaderBasedWrap) { generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT); } + if (description.hasGradient || description.hasLinearTexture) { + shader.append(gFS_Transfer_Functions); + } + if (description.hasBitmap || ((description.hasTexture || description.hasExternalTexture) && + !description.hasAlpha8Texture)) { + shader.append(gFS_OETF[description.hasLinearTexture && !mHasSRGB]); + } + if (description.hasGradient) { + shader.append(gFS_Gradient_Functions); + shader.append(gFS_Gradient_Preamble[mHasSRGB]); + } // Begin the shader shader.append(gFS_Main); { @@ -659,7 +655,8 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (description.hasTexture || description.hasExternalTexture) { if (description.hasAlpha8Texture) { if (!description.hasGradient && !description.hasBitmap) { - shader.append(gFS_Main_FetchA8Texture[modulateOp]); + shader.append( + gFS_Main_FetchA8Texture[modulateOp * 2 + description.hasGammaCorrection]); } } else { shader.append(gFS_Main_FetchTexture[modulateOp]); @@ -671,10 +668,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti } if (description.hasGradient) { shader.append(gFS_Main_FetchGradient[gradientIndex(description)]); - shader.appendFormat(gFS_Main_AddDitherToGradient, gFS_Main_Dither[mHasES3]); } if (description.hasBitmap) { - if (!description.isBitmapNpot) { + if (!description.useShaderBasedWrap) { shader.append(gFS_Main_FetchBitmap); } else { shader.append(gFS_Main_FetchBitmapNpot); @@ -715,6 +711,10 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti } } + if (description.hasGradient) { + shader.append(gFS_Main_AddDither); + } + // Output the fragment if (!blendFramebuffer) { shader.append(gFS_Main_FragColor); @@ -743,12 +743,12 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti return shader; } -void ProgramCache::generateBlend(String8& shader, const char* name, SkXfermode::Mode mode) { +void ProgramCache::generateBlend(String8& shader, const char* name, SkBlendMode mode) { shader.append("\nvec4 "); shader.append(name); shader.append("(vec4 src, vec4 dst) {\n"); shader.append(" "); - shader.append(gBlendOps[mode]); + shader.append(gBlendOps[(int)mode]); shader.append("}\n"); } |