Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8299f683 authored by Romain Guy's avatar Romain Guy Committed by Android (Google) Code Review
Browse files

Merge "Use float textures to render gradients when possible" into jb-mr2-dev

parents c51628aa b4880042
Loading
Loading
Loading
Loading
+25 −20
Original line number Diff line number Diff line
@@ -20,39 +20,47 @@
namespace android {
namespace uirenderer {

///////////////////////////////////////////////////////////////////////////////
// Defines
///////////////////////////////////////////////////////////////////////////////

// Must be a power of two
#define DITHER_KERNEL_SIZE 4

///////////////////////////////////////////////////////////////////////////////
// Lifecycle
///////////////////////////////////////////////////////////////////////////////

void Dither::bindDitherTexture() {
    if (!mInitialized) {
        const uint8_t pattern[] = {
             0,  8,  2, 10,
            12,  4, 14,  6,
             3, 11,  1,  9,
            15,  7, 13,  5
        };
        bool useFloatTexture = Extensions::getInstance().getMajorGlVersion() >= 3;

        glGenTextures(1, &mDitherTexture);
        glBindTexture(GL_TEXTURE_2D, mDitherTexture);

        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

        if (useFloatTexture) {
            float dither = 1.0f / (255.0f * DITHER_KERNEL_SIZE * DITHER_KERNEL_SIZE);
            const GLfloat pattern[] = {
                 0 * dither,  8 * dither,  2 * dither, 10 * dither,
                12 * dither,  4 * dither, 14 * dither,  6 * dither,
                 3 * dither, 11 * dither,  1 * dither,  9 * dither,
                15 * dither,  7 * dither, 13 * dither,  5 * dither
            };

            glPixelStorei(GL_UNPACK_ALIGNMENT, sizeof(GLfloat));
            glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0,
                    GL_RED, GL_FLOAT, &pattern);
        } else {
            const uint8_t pattern[] = {
                 0,  8,  2, 10,
                12,  4, 14,  6,
                 3, 11,  1,  9,
                15,  7, 13,  5
            };

            glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0,
                    GL_ALPHA, GL_UNSIGNED_BYTE, &pattern);
        }

        mInitialized = true;
    } else {
@@ -76,10 +84,7 @@ void Dither::setupProgram(Program* program, GLuint* textureUnit) {

    bindDitherTexture();

    float ditherSize = 1.0f / DITHER_KERNEL_SIZE;
    glUniform1i(program->getUniform("ditherSampler"), textureSlot);
    glUniform1f(program->getUniform("ditherSize"), ditherSize);
    glUniform1f(program->getUniform("ditherSizeSquared"), ditherSize * ditherSize);
}

}; // namespace uirenderer
+11 −1
Original line number Diff line number Diff line
@@ -17,13 +17,23 @@
#ifndef ANDROID_HWUI_DITHER_H
#define ANDROID_HWUI_DITHER_H

#include <GLES2/gl2.h>
#include <GLES3/gl3.h>

#include "Program.h"

namespace android {
namespace uirenderer {

///////////////////////////////////////////////////////////////////////////////
// Defines
///////////////////////////////////////////////////////////////////////////////

// Must be a power of two
#define DITHER_KERNEL_SIZE 4
// These must not use the .0f notation as they are used from GLSL
#define DITHER_KERNEL_SIZE_INV (1.0 / 4.0)
#define DITHER_KERNEL_SIZE_INV_SQUARE (1.0 / 16.0)

/**
 * Handles dithering for programs.
 */
+90 −52
Original line number Diff line number Diff line
@@ -26,13 +26,6 @@
namespace android {
namespace uirenderer {

///////////////////////////////////////////////////////////////////////////////
// Defines
///////////////////////////////////////////////////////////////////////////////

#define GRADIENT_TEXTURE_HEIGHT 2
#define GRADIENT_BYTES_PER_PIXEL 4

///////////////////////////////////////////////////////////////////////////////
// Functions
///////////////////////////////////////////////////////////////////////////////
@@ -83,6 +76,10 @@ GradientCache::GradientCache():
    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);

    mCache.setOnEntryRemovedListener(this);

    const Extensions& extensions = Extensions::getInstance();
    mUseFloatTexture = extensions.getMajorGlVersion() >= 3;
    mHasNpot = extensions.hasNPot();
}

GradientCache::GradientCache(uint32_t maxByteSize):
@@ -120,7 +117,7 @@ void GradientCache::setMaxSize(uint32_t maxSize) {

void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) {
    if (texture) {
        const uint32_t size = texture->width * texture->height * GRADIENT_BYTES_PER_PIXEL;
        const uint32_t size = texture->width * texture->height * bytesPerPixel();
        mSize -= size;

        glDeleteTextures(1, &texture->id);
@@ -151,7 +148,7 @@ void GradientCache::getGradientInfo(const uint32_t* colors, const int count,
        GradientInfo& info) {
    uint32_t width = 256 * (count - 1);

    if (!Extensions::getInstance().hasNPot()) {
    if (!mHasNpot) {
        width = 1 << (31 - __builtin_clz(width));
    }

@@ -175,12 +172,12 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,

    Texture* texture = new Texture;
    texture->width = info.width;
    texture->height = GRADIENT_TEXTURE_HEIGHT;
    texture->height = 2;
    texture->blend = info.hasAlpha;
    texture->generation = 1;

    // Asume the cache is always big enough
    const uint32_t size = texture->width * texture->height * GRADIENT_BYTES_PER_PIXEL;
    const uint32_t size = texture->width * texture->height * bytesPerPixel();
    while (getSize() + size > mMaxSize) {
        mCache.removeOldest();
    }
@@ -193,69 +190,110 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
    return texture;
}

size_t GradientCache::bytesPerPixel() const {
    // We use 4 channels (RGBA)
    return 4 * (mUseFloatTexture ? sizeof(float) : sizeof(uint8_t));
}

void GradientCache::splitToBytes(uint32_t inColor, GradientColor& outColor) const {
    outColor.r = (inColor >> 16) & 0xff;
    outColor.g = (inColor >>  8) & 0xff;
    outColor.b = (inColor >>  0) & 0xff;
    outColor.a = (inColor >> 24) & 0xff;
}

void GradientCache::splitToFloats(uint32_t inColor, GradientColor& outColor) const {
    outColor.r = ((inColor >> 16) & 0xff) / 255.0f;
    outColor.g = ((inColor >>  8) & 0xff) / 255.0f;
    outColor.b = ((inColor >>  0) & 0xff) / 255.0f;
    outColor.a = ((inColor >> 24) & 0xff) / 255.0f;
}

void GradientCache::mixBytes(GradientColor& start, GradientColor& end, float amount,
        uint8_t*& dst) const {
    float oppAmount = 1.0f - amount;
    const float alpha = start.a * oppAmount + end.a * amount;
    const float a = alpha / 255.0f;

    *dst++ = uint8_t(a * (start.r * oppAmount + end.r * amount));
    *dst++ = uint8_t(a * (start.g * oppAmount + end.g * amount));
    *dst++ = uint8_t(a * (start.b * oppAmount + end.b * amount));
    *dst++ = uint8_t(alpha);
}

void GradientCache::mixFloats(GradientColor& start, GradientColor& end, float amount,
        uint8_t*& dst) const {
    float oppAmount = 1.0f - amount;
    const float a = start.a * oppAmount + end.a * amount;

    float* d = (float*) dst;
    *d++ = a * (start.r * oppAmount + end.r * amount);
    *d++ = a * (start.g * oppAmount + end.g * amount);
    *d++ = a * (start.b * oppAmount + end.b * amount);
    *d++ = a;

    dst += 4 * sizeof(float);
}

void GradientCache::generateTexture(uint32_t* colors, float* positions,
        int count, Texture* texture) {

    const uint32_t width = texture->width;
    const GLsizei rowBytes = width * GRADIENT_BYTES_PER_PIXEL;
    uint32_t pixels[width * texture->height];
    const GLsizei rowBytes = width * bytesPerPixel();
    uint8_t pixels[rowBytes * texture->height];

    int currentPos = 1;
    static ChannelSplitter gSplitters[] = {
            &android::uirenderer::GradientCache::splitToBytes,
            &android::uirenderer::GradientCache::splitToFloats,
    };
    ChannelSplitter split = gSplitters[mUseFloatTexture];

    static ChannelMixer gMixers[] = {
            &android::uirenderer::GradientCache::mixBytes,
            &android::uirenderer::GradientCache::mixFloats,
    };
    ChannelMixer mix = gMixers[mUseFloatTexture];

    float startA = (colors[0] >> 24) & 0xff;
    float startR = (colors[0] >> 16) & 0xff;
    float startG = (colors[0] >>  8) & 0xff;
    float startB = (colors[0] >>  0) & 0xff;
    GradientColor start;
    (this->*split)(colors[0], start);

    float endA = (colors[1] >> 24) & 0xff;
    float endR = (colors[1] >> 16) & 0xff;
    float endG = (colors[1] >>  8) & 0xff;
    float endB = (colors[1] >>  0) & 0xff;
    GradientColor end;
    (this->*split)(colors[1], end);

    float start = positions[0];
    float distance = positions[1] - start;
    int currentPos = 1;
    float startPos = positions[0];
    float distance = positions[1] - startPos;

    uint8_t* p = (uint8_t*) pixels;
    uint8_t* dst = pixels;
    for (uint32_t x = 0; x < width; x++) {
        float pos = x / float(width - 1);
        if (pos > positions[currentPos]) {
            startA = endA;
            startR = endR;
            startG = endG;
            startB = endB;
            start = positions[currentPos];
            start = end;
            startPos = positions[currentPos];

            currentPos++;

            endA = (colors[currentPos] >> 24) & 0xff;
            endR = (colors[currentPos] >> 16) & 0xff;
            endG = (colors[currentPos] >>  8) & 0xff;
            endB = (colors[currentPos] >>  0) & 0xff;
            distance = positions[currentPos] - start;
            (this->*split)(colors[currentPos], end);
            distance = positions[currentPos] - startPos;
        }

        float amount = (pos - start) / distance;
        float oppAmount = 1.0f - amount;

        const float alpha = startA * oppAmount + endA * amount;
        const float a = alpha / 255.0f;
        *p++ = uint8_t(a * (startR * oppAmount + endR * amount));
        *p++ = uint8_t(a * (startG * oppAmount + endG * amount));
        *p++ = uint8_t(a * (startB * oppAmount + endB * amount));
        *p++ = uint8_t(alpha);
        float amount = (pos - startPos) / distance;
        (this->*mix)(start, end, amount, dst);
    }

    for (int i = 1; i < GRADIENT_TEXTURE_HEIGHT; i++) {
        memcpy(pixels + width * i, pixels, rowBytes);
    }
    memcpy(pixels + rowBytes, pixels, rowBytes);

    glGenTextures(1, &texture->id);

    glBindTexture(GL_TEXTURE_2D, texture->id);
    glPixelStorei(GL_UNPACK_ALIGNMENT, GRADIENT_BYTES_PER_PIXEL);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

    if (mUseFloatTexture) {
        // We have to use GL_RGBA16F because GL_RGBA32F does not support filtering
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, texture->height, 0,
                GL_RGBA, GL_FLOAT, pixels);
    } else {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, texture->height, 0,
                GL_RGBA, GL_UNSIGNED_BYTE, pixels);
    }

    texture->setFilter(GL_LINEAR);
    texture->setWrap(GL_CLAMP_TO_EDGE);
+24 −1
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@
#ifndef ANDROID_HWUI_GRADIENT_CACHE_H
#define ANDROID_HWUI_GRADIENT_CACHE_H

#include <GLES2/gl2.h>
#include <GLES3/gl3.h>

#include <SkShader.h>

@@ -160,12 +160,35 @@ private:

    void getGradientInfo(const uint32_t* colors, const int count, GradientInfo& info);

    size_t bytesPerPixel() const;

    struct GradientColor {
        float r;
        float g;
        float b;
        float a;
    };

    typedef void (GradientCache::*ChannelSplitter)(uint32_t inColor,
            GradientColor& outColor) const;

    void splitToBytes(uint32_t inColor, GradientColor& outColor) const;
    void splitToFloats(uint32_t inColor, GradientColor& outColor) const;

    typedef void (GradientCache::*ChannelMixer)(GradientColor& start, GradientColor& end,
            float amount, uint8_t*& dst) const;

    void mixBytes(GradientColor& start, GradientColor& end, float amount, uint8_t*& dst) const;
    void mixFloats(GradientColor& start, GradientColor& end, float amount, uint8_t*& dst) const;

    LruCache<GradientCacheEntry, Texture*> mCache;

    uint32_t mSize;
    uint32_t mMaxSize;

    GLint mMaxTextureSize;
    bool mUseFloatTexture;
    bool mHasNpot;

    Vector<SkShader*> mGarbage;
    mutable Mutex mLock;
+40 −48
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <utils/String8.h>

#include "Caches.h"
#include "Dither.h"
#include "ProgramCache.h"

namespace android {
@@ -32,6 +33,9 @@ namespace uirenderer {
#define MODULATE_OP_MODULATE 1
#define MODULATE_OP_MODULATE_A8 2

#define STR(x) STR1(x)
#define STR1(x) #x

///////////////////////////////////////////////////////////////////////////////
// Vertex shaders snippets
///////////////////////////////////////////////////////////////////////////////
@@ -51,17 +55,8 @@ const char* gVS_Header_Uniforms =
        "uniform mat4 transform;\n";
const char* gVS_Header_Uniforms_IsPoint =
        "uniform mediump float pointSize;\n";
const char* gVS_Header_Uniforms_HasGradient[3] = {
        // Linear
        "uniform mat4 screenSpace;\n"
        "uniform float ditherSize;\n",
        // Circular
        "uniform mat4 screenSpace;\n"
        "uniform float ditherSize;\n",
        // Sweep
        "uniform mat4 screenSpace;\n"
        "uniform float ditherSize;\n"
};
const char* gVS_Header_Uniforms_HasGradient =
        "uniform mat4 screenSpace;\n";
const char* gVS_Header_Uniforms_HasBitmap =
        "uniform mat4 textureTransform;\n"
        "uniform mediump vec2 textureDimension;\n";
@@ -105,21 +100,21 @@ const char* gVS_Main_OutTransformedTexCoords =
const char* gVS_Main_OutGradient[6] = {
        // Linear
        "    linear = vec2((screenSpace * position).x, 0.5);\n"
        "    ditherTexCoords = (transform * position).xy * ditherSize;\n",
        "    ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
        "    linear = (screenSpace * position).x;\n"
        "    ditherTexCoords = (transform * position).xy * ditherSize;\n",
        "    ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",

        // Circular
        "    circular = (screenSpace * position).xy;\n"
        "    ditherTexCoords = (transform * position).xy * ditherSize;\n",
        "    ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
        "    circular = (screenSpace * position).xy;\n"
        "    ditherTexCoords = (transform * position).xy * ditherSize;\n",
        "    ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",

        // Sweep
        "    sweep = (screenSpace * position).xy;\n"
        "    ditherTexCoords = (transform * position).xy * ditherSize;\n",
        "    ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
        "    sweep = (screenSpace * position).xy;\n"
        "    ditherTexCoords = (transform * position).xy * ditherSize;\n",
        "    ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
};
const char* gVS_Main_OutBitmapTexCoords =
        "    outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
@@ -153,24 +148,14 @@ const char* gFS_Uniforms_TextureSampler =
        "uniform sampler2D baseSampler;\n";
const char* gFS_Uniforms_ExternalTextureSampler =
        "uniform samplerExternalOES baseSampler;\n";
#define FS_UNIFORMS_DITHER \
        "uniform float ditherSizeSquared;\n" \
        "uniform sampler2D ditherSampler;\n"
#define FS_UNIFORMS_GRADIENT \
        "uniform vec4 startColor;\n" \
const char* gFS_Uniforms_Dither =
        "uniform sampler2D ditherSampler;";
const char* gFS_Uniforms_GradientSampler[2] = {
        "%s\n"
        "uniform sampler2D gradientSampler;\n",
        "%s\n"
        "uniform vec4 startColor;\n"
        "uniform vec4 endColor;\n"
const char* gFS_Uniforms_GradientSampler[6] = {
        // Linear
        FS_UNIFORMS_DITHER "uniform sampler2D gradientSampler;\n",
        FS_UNIFORMS_DITHER FS_UNIFORMS_GRADIENT,

        // Circular
        FS_UNIFORMS_DITHER "uniform sampler2D gradientSampler;\n",
        FS_UNIFORMS_DITHER FS_UNIFORMS_GRADIENT,

        // Sweep
        FS_UNIFORMS_DITHER "uniform sampler2D gradientSampler;\n",
        FS_UNIFORMS_DITHER FS_UNIFORMS_GRADIENT
};
const char* gFS_Uniforms_BitmapSampler =
        "uniform sampler2D bitmapSampler;\n";
@@ -197,10 +182,14 @@ const char* gFS_Main_PointBitmapTexCoords =
        "    highp vec2 outBitmapTexCoords = outPointBitmapTexCoords + "
        "((gl_PointCoord - vec2(0.5, 0.5)) * textureDimension * vec2(pointSize, pointSize));\n";

#define FS_MAIN_DITHER \
        "texture2D(ditherSampler, ditherTexCoords).a * ditherSizeSquared"
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).r"
};
const char* gFS_Main_AddDitherToGradient =
        "    gradientColor += " FS_MAIN_DITHER ";\n";
        "    gradientColor += %s;\n";

// Fast cases
const char* gFS_Fast_SingleColor =
@@ -233,18 +222,18 @@ const char* gFS_Fast_SingleModulateA8Texture_ApplyGamma =
        "}\n\n";
const char* gFS_Fast_SingleGradient[2] = {
        "\nvoid main(void) {\n"
        "    gl_FragColor = " FS_MAIN_DITHER " + texture2D(gradientSampler, linear);\n"
        "    gl_FragColor = %s + texture2D(gradientSampler, linear);\n"
        "}\n\n",
        "\nvoid main(void) {\n"
        "    gl_FragColor = " FS_MAIN_DITHER " + mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
        "}\n\n"
        "    gl_FragColor = %s + mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
        "}\n\n",
};
const char* gFS_Fast_SingleModulateGradient[2] = {
        "\nvoid main(void) {\n"
        "    gl_FragColor = " FS_MAIN_DITHER " + color.a * texture2D(gradientSampler, linear);\n"
        "    gl_FragColor = %s + color.a * texture2D(gradientSampler, linear);\n"
        "}\n\n",
        "\nvoid main(void) {\n"
        "    gl_FragColor = " FS_MAIN_DITHER " + color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
        "    gl_FragColor = %s + color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
        "}\n\n"
};

@@ -410,7 +399,7 @@ const char* gBlendOps[18] = {
// Constructors/destructors
///////////////////////////////////////////////////////////////////////////////

ProgramCache::ProgramCache() {
ProgramCache::ProgramCache(): mHasES3(Extensions::getInstance().getMajorGlVersion() >= 3) {
}

ProgramCache::~ProgramCache() {
@@ -484,7 +473,7 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
        shader.append(gVS_Header_Uniforms_TextureTransform);
    }
    if (description.hasGradient) {
        shader.append(gVS_Header_Uniforms_HasGradient[description.gradientType]);
        shader.append(gVS_Header_Uniforms_HasGradient);
    }
    if (description.hasBitmap) {
        shader.append(gVS_Header_Uniforms_HasBitmap);
@@ -601,7 +590,8 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
        shader.append(gFS_Uniforms_ExternalTextureSampler);
    }
    if (description.hasGradient) {
        shader.append(gFS_Uniforms_GradientSampler[gradientIndex(description)]);
        shader.appendFormat(gFS_Uniforms_GradientSampler[description.isSimpleGradient],
                gFS_Uniforms_Dither);
    }
    if (description.hasBitmap && description.isPoint) {
        shader.append(gFS_Header_Uniforms_PointHasBitmap);
@@ -652,9 +642,11 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
            fast = true;
        } else if (singleGradient) {
            if (!description.modulate) {
                shader.append(gFS_Fast_SingleGradient[description.isSimpleGradient]);
                shader.appendFormat(gFS_Fast_SingleGradient[description.isSimpleGradient],
                        gFS_Main_Dither[mHasES3]);
            } else {
                shader.append(gFS_Fast_SingleModulateGradient[description.isSimpleGradient]);
                shader.appendFormat(gFS_Fast_SingleModulateGradient[description.isSimpleGradient],
                        gFS_Main_Dither[mHasES3]);
            }
            fast = true;
        }
@@ -708,7 +700,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
        }
        if (description.hasGradient) {
            shader.append(gFS_Main_FetchGradient[gradientIndex(description)]);
            shader.append(gFS_Main_AddDitherToGradient);
            shader.appendFormat(gFS_Main_AddDitherToGradient, gFS_Main_Dither[mHasES3]);
        }
        if (description.hasBitmap) {
            if (description.isPoint) {
Loading