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

Commit 8ce00301 authored by Romain Guy's avatar Romain Guy
Browse files

Implement clipRect with a transform, clipRegion & clipPath

Bug #7146141

When non-rectangular clipping occurs in a layer the render buffer
used as the stencil buffer is not cached. If this happens on a
View's hardware layer the render buffer will live for as long
as the layer is bound to the view. When a stencil buffer is
required because of a call to Canvas.saveLayer() it will be allocated
on every frame. A future change will address this problem.

If "show GPU overdraw" is enabled, non-rectangular clips are not
supported anymore and we fall back to rectangular clips instead.
This is a limitation imposed by OpenGL ES that cannot be worked
around at this time.

This change also improves the Matrix4 implementation to easily
detect when a rect remains a rect after transform.

Change-Id: I0e69fb901792d38bc0c4ca1bf9fdb02d7db415b9
parent 0f8d1553
Loading
Loading
Loading
Loading
+2 −23
Original line number Diff line number Diff line
@@ -172,17 +172,6 @@ public abstract class HardwareRenderer {
     */
    public static final String DEBUG_SHOW_OVERDRAW_PROPERTY = "debug.hwui.show_overdraw";

    /**
     * Turn on to allow region clipping (see
     * {@link android.graphics.Canvas#clipPath(android.graphics.Path)} and
     * {@link android.graphics.Canvas#clipRegion(android.graphics.Region)}.
     *
     * When this option is turned on a stencil buffer is always required.
     * If this option is off a stencil buffer is only created when the overdraw
     * debugging mode is turned on.
     */
    private static final boolean REGION_CLIPPING_ENABLED = false;

    /**
     * A process can set this flag to false to prevent the use of hardware
     * rendering.
@@ -885,15 +874,6 @@ public abstract class HardwareRenderer {
            if (value != mShowOverdraw) {
                changed = true;
                mShowOverdraw = value;

                if (!REGION_CLIPPING_ENABLED) {
                    if (surface != null && isEnabled()) {
                        if (validate()) {
                            sEglConfig = loadEglConfig();
                            invalidate(surface);
                        }
                    }
                }
            }

            if (nLoadProperties()) {
@@ -1764,9 +1744,8 @@ public abstract class HardwareRenderer {

        @Override
        int[] getConfig(boolean dirtyRegions) {
            //noinspection PointlessBooleanExpression
            final int stencilSize = mShowOverdraw || REGION_CLIPPING_ENABLED ?
                    GLES20Canvas.getStencilSize() : 0;
            //noinspection PointlessBooleanExpression,ConstantConditions
            final int stencilSize = GLES20Canvas.getStencilSize();
            final int swapBehavior = dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;

            return new int[] {
+0 −2
Original line number Diff line number Diff line
@@ -270,9 +270,7 @@ public:
    GammaFontRenderer* fontRenderer;

    Dither dither;
#if STENCIL_BUFFER_SIZE
    Stencil stencil;
#endif

    // Debug methods
    PFNGLINSERTEVENTMARKEREXTPROC eventMark;
+16 −4
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight) {
    renderer = NULL;
    displayList = NULL;
    fbo = 0;
    stencil = 0;
    debugDrawUpdate = false;
    Caches::getInstance().resourceCache.incrementRefcount(this);
}
@@ -53,9 +54,22 @@ Layer::~Layer() {
    deleteTexture();
}

void Layer::removeFbo() {
void Layer::removeFbo(bool flush) {
    if (stencil) {
        // TODO: recycle & cache instead of simply deleting
        GLuint previousFbo;
        glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
        if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
        if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);

        glDeleteRenderbuffers(1, &stencil);
        stencil = 0;
    }

    if (fbo) {
        LayerRenderer::flushLayer(this);
        if (flush) LayerRenderer::flushLayer(this);
        // If put fails the cache will delete the FBO
        Caches::getInstance().fboCache.put(fbo);
        fbo = 0;
    }
@@ -75,7 +89,5 @@ void Layer::setColorFilter(SkiaColorFilter* filter) {
    }
}



}; // namespace uirenderer
}; // namespace android
+20 −5
Original line number Diff line number Diff line
@@ -48,7 +48,12 @@ struct Layer {
    Layer(const uint32_t layerWidth, const uint32_t layerHeight);
    ~Layer();

    void removeFbo();
    /**
     * Calling this method will remove (either by recycling or
     * destroying) the associated FBO, if present, and any render
     * buffer (stencil for instance.)
     */
    void removeFbo(bool flush = true);

    /**
     * Sets this layer's region to a rectangle. Computes the appropriate
@@ -134,6 +139,14 @@ struct Layer {
        return fbo;
    }

    inline void setStencilRenderBuffer(GLuint renderBuffer) {
        this->stencil = renderBuffer;
    }

    inline GLuint getStencilRenderBuffer() {
        return stencil;
    }

    inline GLuint getTexture() {
        return texture.id;
    }
@@ -212,10 +225,6 @@ struct Layer {
        texture.id = 0;
    }

    inline void deleteFbo() {
        if (fbo) glDeleteFramebuffers(1, &fbo);
    }

    inline void allocateTexture(GLenum format, GLenum storage) {
#if DEBUG_LAYERS
        ALOGD("  Allocate layer: %dx%d", getWidth(), getHeight());
@@ -274,6 +283,12 @@ private:
     */
    GLuint fbo;

    /**
     * Name of the render buffer used as the stencil buffer. If the
     * name is 0, this layer does not have a stencil buffer.
     */
    GLuint stencil;

    /**
     * Indicates whether this layer has been used already.
     */
+9 −1
Original line number Diff line number Diff line
@@ -100,13 +100,21 @@ bool LayerRenderer::suppressErrorChecks() {
}

///////////////////////////////////////////////////////////////////////////////
// Dirty region tracking
// Layer support
///////////////////////////////////////////////////////////////////////////////

bool LayerRenderer::hasLayer() {
    return true;
}

void LayerRenderer::ensureStencilBuffer() {
    attachStencilBufferToLayer(mLayer);
}

///////////////////////////////////////////////////////////////////////////////
// Dirty region tracking
///////////////////////////////////////////////////////////////////////////////

Region* LayerRenderer::getRegion() {
    if (getSnapshot()->flags & Snapshot::kFlagFboTarget) {
        return OpenGLRenderer::getRegion();
Loading