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

Commit a5aed0d5 authored by Romain Guy's avatar Romain Guy
Browse files

Add support for advanced blend modes with the framebuffer.

This adds the ability to blend with the framebuffer using Darken,
Lighten, Add, Multiply, Overlay and Screen.

Change-Id: Iae01a53797d4ad39c373cba6ff2a42293129da1a
parent d90f23e2
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -26,17 +26,33 @@
namespace android {
namespace uirenderer {

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

// Debug
#define DEBUG_EXTENSIONS 0

// Debug
#if DEBUG_EXTENSIONS
    #define EXT_LOGD(...) LOGD(__VA_ARGS__)
#else
    #define EXT_LOGD(...)
#endif

class Extensions {
public:
    Extensions() {
        const char* buffer = (const char*) glGetString(GL_EXTENSIONS);
        const char* current = buffer;
        const char* head = current;
        EXT_LOGD("Available GL extensions:");
        do {
            head = strchr(current, ' ');
            String8 s(current, head ? head - current : strlen(current));
            if (s.length()) {
                mExtensionList.add(s);
                EXT_LOGD("  %s", s.string());
            }
            current = head + 1;
        } while (head);
@@ -44,6 +60,7 @@ public:
        mHasNPot = hasExtension("GL_OES_texture_npot");
        mHasDrawPath = hasExtension("GL_NV_draw_path");
        mHasCoverageSample = hasExtension("GL_NV_coverage_sample");
        mHasFramebufferFetch = hasExtension("GL_NV_shader_framebuffer_fetch");

        mExtensions = buffer;
    }
@@ -51,6 +68,7 @@ public:
    inline bool hasNPot() const { return mHasNPot; }
    inline bool hasDrawPath() const { return mHasDrawPath; }
    inline bool hasCoverageSample() const { return mHasCoverageSample; }
    inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; }

    bool hasExtension(const char* extension) const {
        const String8 s(extension);
@@ -69,6 +87,7 @@ private:
    bool mHasNPot;
    bool mHasDrawPath;
    bool mHasCoverageSample;
    bool mHasFramebufferFetch;
}; // class Extensions

}; // namespace uirenderer
+64 −33
Original line number Diff line number Diff line
@@ -240,11 +240,15 @@ int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,

    if (p) {
        alpha = p->getAlpha();
        if (!mExtensions.hasFramebufferFetch()) {
            const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
            if (!isMode) {
                // Assume SRC_OVER
                mode = SkXfermode::kSrcOver_Mode;
            }
        } else {
            mode = getXfermode(p->getXfermode());
        }
    } else {
        mode = SkXfermode::kSrcOver_Mode;
    }
@@ -519,12 +523,15 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom,
    }

    SkXfermode::Mode mode;

    if (!mExtensions.hasFramebufferFetch()) {
        const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
        if (!isMode) {
            // Assume SRC_OVER
            mode = SkXfermode::kSrcOver_Mode;
        }
    } else {
        mode = getXfermode(p->getXfermode());
    }

    // Skia draws using the color's alpha channel if < 255
    // Otherwise, it uses the paint's alpha
@@ -731,11 +738,12 @@ void OpenGLRenderer::setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t
         }
     }

     // Setup the blending mode
     chooseBlending(true, mode, description);

     // Build and use the appropriate shader
     useProgram(mCaches.programCache.get(description));

     // Setup the blending mode
     chooseBlending(true, mode);
     bindTexture(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, textureUnit);
     glUniform1i(mCaches.currentProgram->getUniform("sampler"), textureUnit);

@@ -837,9 +845,6 @@ void OpenGLRenderer::drawColorRect(float left, float top, float right, float bot

    GLuint textureUnit = 0;

    // Setup the blending mode
    chooseBlending(alpha < 255 || (mShader && mShader->blend()), mode);

    // Describe the required shaders
    ProgramDescription description;
    if (mShader) {
@@ -849,6 +854,9 @@ void OpenGLRenderer::drawColorRect(float left, float top, float right, float bot
        mColorFilter->describe(description, mExtensions);
    }

    // Setup the blending mode
    chooseBlending(alpha < 255 || (mShader && mShader->blend()), mode, description);

    // Build and use the appropriate shader
    useProgram(mCaches.programCache.get(description));

@@ -907,11 +915,11 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b
    mModelView.loadTranslate(left, top, 0.0f);
    mModelView.scale(right - left, bottom - top, 1.0f);

    chooseBlending(blend || alpha < 1.0f, mode, description);

    useProgram(mCaches.programCache.get(description));
    mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);

    chooseBlending(blend || alpha < 1.0f, mode);

    // Texture
    bindTexture(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, 0);
    glUniform1i(mCaches.currentProgram->getUniform("sampler"), 0);
@@ -939,24 +947,36 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b
    glDisableVertexAttribArray(texCoordsSlot);
}

void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, bool isPremultiplied) {
void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode,
        ProgramDescription& description) {
    blend = blend || mode != SkXfermode::kSrcOver_Mode;
    if (blend) {
        if (mode < SkXfermode::kPlus_Mode) {
            if (!mCaches.blend) {
                glEnable(GL_BLEND);
            }

            GLenum sourceMode = gBlends[mode].src;
            GLenum destMode = gBlends[mode].dst;
        if (!isPremultiplied && sourceMode == GL_ONE) {
            sourceMode = GL_SRC_ALPHA;
        }

            if (sourceMode != mCaches.lastSrcMode || destMode != mCaches.lastDstMode) {
                glBlendFunc(sourceMode, destMode);
                mCaches.lastSrcMode = sourceMode;
                mCaches.lastDstMode = destMode;
            }
        } else {
            // These blend modes are not supported by OpenGL directly and have
            // to be implemented using shaders. Since the shader will perform
            // the blending, turn blending off here
            if (mExtensions.hasFramebufferFetch()) {
                description.framebufferMode = mode;
            }

            if (mCaches.blend) {
                glDisable(GL_BLEND);
            }
            blend = false;
        }
    } else if (mCaches.blend) {
        glDisable(GL_BLEND);
    }
@@ -983,11 +1003,15 @@ void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, flo

void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) {
    if (paint) {
        if (!mExtensions.hasFramebufferFetch()) {
            const bool isMode = SkXfermode::IsMode(paint->getXfermode(), mode);
            if (!isMode) {
                // Assume SRC_OVER
                *mode = SkXfermode::kSrcOver_Mode;
            }
        } else {
            *mode = getXfermode(paint->getXfermode());
        }

        // Skia draws using the color's alpha channel if < 255
        // Otherwise, it uses the paint's alpha
@@ -1002,6 +1026,13 @@ void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermod
    }
}

SkXfermode::Mode OpenGLRenderer::getXfermode(SkXfermode* mode) {
    if (mode == NULL) {
        return SkXfermode::kSrcOver_Mode;
    }
    return mode->fMode;
}

void OpenGLRenderer::bindTexture(GLuint texture, GLenum wrapS, GLenum wrapT, GLuint textureUnit) {
    glActiveTexture(gTextureUnits[textureUnit]);
    glBindTexture(GL_TEXTURE_2D, texture);
+3 −1
Original line number Diff line number Diff line
@@ -322,7 +322,9 @@ private:
     * Enable or disable blending as necessary. This function sets the appropriate
     * blend function based on the specified xfermode.
     */
    inline void chooseBlending(bool blend, SkXfermode::Mode mode, bool isPremultiplied = true);
    inline void chooseBlending(bool blend, SkXfermode::Mode mode, ProgramDescription& description);

    inline SkXfermode::Mode getXfermode(SkXfermode* mode);

    /**
     * Use the specified program with the current GL context. If the program is already
+20 −2
Original line number Diff line number Diff line
@@ -66,6 +66,8 @@ const char* gVS_Footer =
// Fragment shaders snippets
///////////////////////////////////////////////////////////////////////////////

const char* gFS_Header_Extension_FramebufferFetch =
        "#extension GL_NV_shader_framebuffer_fetch : enable\n\n";
const char* gFS_Header =
        "precision mediump float;\n\n";
const char* gFS_Uniforms_Color =
@@ -115,6 +117,8 @@ const char* gFS_Main_BitmapShader_Modulate =
        "    fragColor = bitmapColor * fragColor.a;\n";
const char* gFS_Main_FragColor =
        "    gl_FragColor = fragColor;\n";
const char* gFS_Main_FragColor_Blend =
        "    gl_FragColor = blendFramebuffer(fragColor, gl_LastFragColor);\n";
const char* gFS_Main_ApplyColorOp[4] = {
        // None
        "",
@@ -281,7 +285,14 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description

String8 ProgramCache::generateFragmentShader(const ProgramDescription& description) {
    // Set the default precision
    String8 shader(gFS_Header);
    String8 shader;

    bool blendFramebuffer = description.framebufferMode >= SkXfermode::kPlus_Mode;
    if (blendFramebuffer) {
        shader.append(gFS_Header_Extension_FramebufferFetch);
    }

    shader.append(gFS_Header);

    // Varyings
    if (description.hasTexture) {
@@ -315,6 +326,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
    if (description.colorOp == ProgramDescription::kColorBlend) {
        generateBlend(shader, "blendColors", description.colorMode);
    }
    if (blendFramebuffer) {
        generateBlend(shader, "blendFramebuffer", description.framebufferMode);
    }
    if (description.isBitmapNpot) {
        generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT);
    }
@@ -359,7 +373,11 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
        // Apply the color op if needed
        shader.append(gFS_Main_ApplyColorOp[description.colorOp]);
        // Output the fragment
        if (!blendFramebuffer) {
            shader.append(gFS_Main_FragColor);
        } else {
            shader.append(gFS_Main_FragColor_Blend);
        }
    }
    // End the shader
    shader.append(gFS_Footer);
+9 −2
Original line number Diff line number Diff line
@@ -35,7 +35,7 @@ namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////

// Debug
#define DEBUG_PROGRAM_CACHE 0
#define DEBUG_PROGRAM_CACHE 1

// Debug
#if DEBUG_PROGRAM_CACHE
@@ -61,6 +61,7 @@ namespace uirenderer {
#define PROGRAM_MAX_XFERMODE 0x1f
#define PROGRAM_XFERMODE_SHADER_SHIFT 26
#define PROGRAM_XFERMODE_COLOR_OP_SHIFT 20
#define PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT 14

#define PROGRAM_BITMAP_WRAPS_SHIFT 9
#define PROGRAM_BITMAP_WRAPT_SHIFT 11
@@ -93,7 +94,8 @@ struct ProgramDescription {
        hasBitmap(false), isBitmapNpot(false), hasGradient(false),
        shadersMode(SkXfermode::kClear_Mode), isBitmapFirst(false),
        bitmapWrapS(GL_CLAMP_TO_EDGE), bitmapWrapT(GL_CLAMP_TO_EDGE),
        colorOp(kColorNone), colorMode(SkXfermode::kClear_Mode) {
        colorOp(kColorNone), colorMode(SkXfermode::kClear_Mode),
        framebufferMode(SkXfermode::kClear_Mode) {
    }

    // Texturing
@@ -113,6 +115,10 @@ struct ProgramDescription {
    int colorOp;
    SkXfermode::Mode colorMode;

    // Framebuffer blending (requires Extensions.hasFramebufferFetch())
    // Ignored for all values < SkXfermode::kPlus_Mode
    SkXfermode::Mode framebufferMode;

    inline uint32_t getEnumForWrap(GLenum wrap) const {
        switch (wrap) {
            case GL_CLAMP_TO_EDGE:
@@ -156,6 +162,7 @@ struct ProgramDescription {
            case kColorNone:
                break;
        }
        key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
        return key;
    }
}; // struct ProgramDescription
Loading