Loading libs/hwui/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ hwui_src_files := \ service/GraphicsStatsService.cpp \ thread/TaskManager.cpp \ utils/Blur.cpp \ utils/Color.cpp \ utils/GLUtils.cpp \ utils/LinearAllocator.cpp \ utils/StringUtils.cpp \ Loading libs/hwui/GlLayer.h +1 −1 Original line number Diff line number Diff line Loading @@ -44,7 +44,7 @@ public: } void setSize(uint32_t width, uint32_t height) override { texture.updateSize(width, height, texture.internalFormat(), texture.format(), texture.updateLayout(width, height, texture.internalFormat(), texture.format(), texture.target()); } Loading libs/hwui/GlopBuilder.cpp +5 −1 Original line number Diff line number Diff line Loading @@ -605,7 +605,11 @@ void GlopBuilder::build() { } else { mDescription.hasExternalTexture = true; } mDescription.hasLinearTexture = mOutGlop->fill.texture.texture->isLinear(); Texture* texture = mOutGlop->fill.texture.texture; mDescription.hasLinearTexture = texture->isLinear(); mDescription.hasColorSpaceConversion = texture->hasColorSpaceConversion(); mDescription.transferFunction = texture->getTransferFunctionType(); mDescription.hasTranslucentConversion = texture->blend; } mDescription.hasColors = mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Color; Loading libs/hwui/Program.h +35 −18 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ #include "FloatColor.h" #include "Matrix.h" #include "Properties.h" #include "utils/Color.h" namespace android { namespace uirenderer { Loading Loading @@ -56,11 +57,11 @@ namespace uirenderer { #define PROGRAM_KEY_BITMAP_NPOT 0x80 #define PROGRAM_KEY_BITMAP_EXTERNAL 0x100 #define PROGRAM_KEY_SWAP_SRC_DST 0x2000 #define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600 #define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800 #define PROGRAM_KEY_SWAP_SRC_DST_SHIFT 13 // Encode the xfermodes on 6 bits #define PROGRAM_MAX_XFERMODE 0x1f #define PROGRAM_XFERMODE_SHADER_SHIFT 26 Loading Loading @@ -89,6 +90,10 @@ namespace uirenderer { #define PROGRAM_HAS_GAMMA_CORRECTION 44 #define PROGRAM_HAS_LINEAR_TEXTURE 45 #define PROGRAM_HAS_COLOR_SPACE_CONVERSION 46 #define PROGRAM_TRANSFER_FUNCTION 47 // 2 bits for transfer function #define PROGRAM_HAS_TRANSLUCENT_CONVERSION 49 /////////////////////////////////////////////////////////////////////////////// // Types /////////////////////////////////////////////////////////////////////////////// Loading @@ -105,13 +110,13 @@ typedef uint64_t programid; * A ProgramDescription must be used in conjunction with a ProgramCache. */ struct ProgramDescription { enum class ColorFilterMode { enum class ColorFilterMode : int8_t { None = 0, Matrix, Blend }; enum Gradient { enum Gradient : int8_t { kGradientLinear = 0, kGradientCircular, kGradientSweep Loading Loading @@ -168,6 +173,11 @@ struct ProgramDescription { // Set when sampling an image in linear space bool hasLinearTexture; bool hasColorSpaceConversion; TransferFunctionType transferFunction; // Indicates whether the bitmap to convert between color spaces is translucent bool hasTranslucentConversion; /** * Resets this description. All fields are reset back to the default * values they hold after building a new instance. Loading Loading @@ -210,6 +220,10 @@ struct ProgramDescription { hasGammaCorrection = false; hasLinearTexture = false; hasColorSpaceConversion = false; transferFunction = TransferFunctionType::None; hasTranslucentConversion = false; } /** Loading Loading @@ -269,18 +283,21 @@ struct ProgramDescription { break; } key |= ((int) framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT; if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST; if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT; if (hasVertexAlpha) key |= programid(0x1) << PROGRAM_HAS_VERTEX_ALPHA_SHIFT; if (useShadowAlphaInterp) key |= programid(0x1) << PROGRAM_USE_SHADOW_ALPHA_INTERP_SHIFT; if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT; if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT; if (isSimpleGradient) key |= programid(0x1) << PROGRAM_IS_SIMPLE_GRADIENT; if (hasColors) key |= programid(0x1) << PROGRAM_HAS_COLORS; if (hasDebugHighlight) key |= programid(0x1) << PROGRAM_HAS_DEBUG_HIGHLIGHT; if (hasRoundRectClip) key |= programid(0x1) << PROGRAM_HAS_ROUND_RECT_CLIP; if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION; if (hasLinearTexture) key |= programid(0x1) << PROGRAM_HAS_LINEAR_TEXTURE; key |= programid(swapSrcDst) << PROGRAM_KEY_SWAP_SRC_DST_SHIFT; key |= programid(modulate) << PROGRAM_MODULATE_SHIFT; key |= programid(hasVertexAlpha) << PROGRAM_HAS_VERTEX_ALPHA_SHIFT; key |= programid(useShadowAlphaInterp) << PROGRAM_USE_SHADOW_ALPHA_INTERP_SHIFT; key |= programid(hasExternalTexture) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT; key |= programid(hasTextureTransform) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT; key |= programid(isSimpleGradient) << PROGRAM_IS_SIMPLE_GRADIENT; key |= programid(hasColors) << PROGRAM_HAS_COLORS; key |= programid(hasDebugHighlight) << PROGRAM_HAS_DEBUG_HIGHLIGHT; key |= programid(hasRoundRectClip) << PROGRAM_HAS_ROUND_RECT_CLIP; key |= programid(hasGammaCorrection) << PROGRAM_HAS_GAMMA_CORRECTION; key |= programid(hasLinearTexture) << PROGRAM_HAS_LINEAR_TEXTURE; key |= programid(hasColorSpaceConversion) << PROGRAM_HAS_COLOR_SPACE_CONVERSION; key |= programid(transferFunction) << PROGRAM_TRANSFER_FUNCTION; key |= programid(hasTranslucentConversion) << PROGRAM_HAS_TRANSLUCENT_CONVERSION; return key; } Loading libs/hwui/ProgramCache.cpp +118 −19 Original line number Diff line number Diff line Loading @@ -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", R"__SHADER__( vec4 OETF(const vec4 linear) { return linear; } )__SHADER__", // 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 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; Loading @@ -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)); Loading @@ -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) { Loading Loading @@ -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 Loading Loading @@ -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 = Loading Loading @@ -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); Loading @@ -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 Loading Loading
libs/hwui/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ hwui_src_files := \ service/GraphicsStatsService.cpp \ thread/TaskManager.cpp \ utils/Blur.cpp \ utils/Color.cpp \ utils/GLUtils.cpp \ utils/LinearAllocator.cpp \ utils/StringUtils.cpp \ Loading
libs/hwui/GlLayer.h +1 −1 Original line number Diff line number Diff line Loading @@ -44,7 +44,7 @@ public: } void setSize(uint32_t width, uint32_t height) override { texture.updateSize(width, height, texture.internalFormat(), texture.format(), texture.updateLayout(width, height, texture.internalFormat(), texture.format(), texture.target()); } Loading
libs/hwui/GlopBuilder.cpp +5 −1 Original line number Diff line number Diff line Loading @@ -605,7 +605,11 @@ void GlopBuilder::build() { } else { mDescription.hasExternalTexture = true; } mDescription.hasLinearTexture = mOutGlop->fill.texture.texture->isLinear(); Texture* texture = mOutGlop->fill.texture.texture; mDescription.hasLinearTexture = texture->isLinear(); mDescription.hasColorSpaceConversion = texture->hasColorSpaceConversion(); mDescription.transferFunction = texture->getTransferFunctionType(); mDescription.hasTranslucentConversion = texture->blend; } mDescription.hasColors = mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Color; Loading
libs/hwui/Program.h +35 −18 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ #include "FloatColor.h" #include "Matrix.h" #include "Properties.h" #include "utils/Color.h" namespace android { namespace uirenderer { Loading Loading @@ -56,11 +57,11 @@ namespace uirenderer { #define PROGRAM_KEY_BITMAP_NPOT 0x80 #define PROGRAM_KEY_BITMAP_EXTERNAL 0x100 #define PROGRAM_KEY_SWAP_SRC_DST 0x2000 #define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600 #define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800 #define PROGRAM_KEY_SWAP_SRC_DST_SHIFT 13 // Encode the xfermodes on 6 bits #define PROGRAM_MAX_XFERMODE 0x1f #define PROGRAM_XFERMODE_SHADER_SHIFT 26 Loading Loading @@ -89,6 +90,10 @@ namespace uirenderer { #define PROGRAM_HAS_GAMMA_CORRECTION 44 #define PROGRAM_HAS_LINEAR_TEXTURE 45 #define PROGRAM_HAS_COLOR_SPACE_CONVERSION 46 #define PROGRAM_TRANSFER_FUNCTION 47 // 2 bits for transfer function #define PROGRAM_HAS_TRANSLUCENT_CONVERSION 49 /////////////////////////////////////////////////////////////////////////////// // Types /////////////////////////////////////////////////////////////////////////////// Loading @@ -105,13 +110,13 @@ typedef uint64_t programid; * A ProgramDescription must be used in conjunction with a ProgramCache. */ struct ProgramDescription { enum class ColorFilterMode { enum class ColorFilterMode : int8_t { None = 0, Matrix, Blend }; enum Gradient { enum Gradient : int8_t { kGradientLinear = 0, kGradientCircular, kGradientSweep Loading Loading @@ -168,6 +173,11 @@ struct ProgramDescription { // Set when sampling an image in linear space bool hasLinearTexture; bool hasColorSpaceConversion; TransferFunctionType transferFunction; // Indicates whether the bitmap to convert between color spaces is translucent bool hasTranslucentConversion; /** * Resets this description. All fields are reset back to the default * values they hold after building a new instance. Loading Loading @@ -210,6 +220,10 @@ struct ProgramDescription { hasGammaCorrection = false; hasLinearTexture = false; hasColorSpaceConversion = false; transferFunction = TransferFunctionType::None; hasTranslucentConversion = false; } /** Loading Loading @@ -269,18 +283,21 @@ struct ProgramDescription { break; } key |= ((int) framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT; if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST; if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT; if (hasVertexAlpha) key |= programid(0x1) << PROGRAM_HAS_VERTEX_ALPHA_SHIFT; if (useShadowAlphaInterp) key |= programid(0x1) << PROGRAM_USE_SHADOW_ALPHA_INTERP_SHIFT; if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT; if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT; if (isSimpleGradient) key |= programid(0x1) << PROGRAM_IS_SIMPLE_GRADIENT; if (hasColors) key |= programid(0x1) << PROGRAM_HAS_COLORS; if (hasDebugHighlight) key |= programid(0x1) << PROGRAM_HAS_DEBUG_HIGHLIGHT; if (hasRoundRectClip) key |= programid(0x1) << PROGRAM_HAS_ROUND_RECT_CLIP; if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION; if (hasLinearTexture) key |= programid(0x1) << PROGRAM_HAS_LINEAR_TEXTURE; key |= programid(swapSrcDst) << PROGRAM_KEY_SWAP_SRC_DST_SHIFT; key |= programid(modulate) << PROGRAM_MODULATE_SHIFT; key |= programid(hasVertexAlpha) << PROGRAM_HAS_VERTEX_ALPHA_SHIFT; key |= programid(useShadowAlphaInterp) << PROGRAM_USE_SHADOW_ALPHA_INTERP_SHIFT; key |= programid(hasExternalTexture) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT; key |= programid(hasTextureTransform) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT; key |= programid(isSimpleGradient) << PROGRAM_IS_SIMPLE_GRADIENT; key |= programid(hasColors) << PROGRAM_HAS_COLORS; key |= programid(hasDebugHighlight) << PROGRAM_HAS_DEBUG_HIGHLIGHT; key |= programid(hasRoundRectClip) << PROGRAM_HAS_ROUND_RECT_CLIP; key |= programid(hasGammaCorrection) << PROGRAM_HAS_GAMMA_CORRECTION; key |= programid(hasLinearTexture) << PROGRAM_HAS_LINEAR_TEXTURE; key |= programid(hasColorSpaceConversion) << PROGRAM_HAS_COLOR_SPACE_CONVERSION; key |= programid(transferFunction) << PROGRAM_TRANSFER_FUNCTION; key |= programid(hasTranslucentConversion) << PROGRAM_HAS_TRANSLUCENT_CONVERSION; return key; } Loading
libs/hwui/ProgramCache.cpp +118 −19 Original line number Diff line number Diff line Loading @@ -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", R"__SHADER__( vec4 OETF(const vec4 linear) { return linear; } )__SHADER__", // 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 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; Loading @@ -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)); Loading @@ -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) { Loading Loading @@ -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 Loading Loading @@ -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 = Loading Loading @@ -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); Loading @@ -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 Loading