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

Commit 9ace8f5e authored by Romain Guy's avatar Romain Guy
Browse files

Use NEAREST filtering for layers whenever possible.

Change-Id: Id5bee1bd4a322cf93e8000b08e18f1e1b058648e
parent f61970fc
Loading
Loading
Loading
Loading
+14 −3
Original line number Diff line number Diff line
@@ -248,6 +248,17 @@ class GLES20Canvas extends HardwareCanvas {

    private static native boolean nIsBackBufferPreserved();

    /**
     * Disables v-sync. For performance testing only.
     * 
     * @hide
     */
    public static void disableVsync() {
        nDisableVsync();
    }

    private static native void nDisableVsync();

    @Override
    void onPreDraw(Rect dirty) {
        if (dirty != null) {
+26 −0
Original line number Diff line number Diff line
@@ -58,6 +58,16 @@ public abstract class HardwareRenderer {
     */
    static final String RENDER_DIRTY_REGIONS_PROPERTY = "hwui.render_dirty_regions";
    
    /**
     * System property used to enable or disable vsync.
     * The default value of this property is assumed to be false.
     * 
     * Possible values:
     * "true", to disable vsync
     * "false", to enable vsync
     */
    static final String DISABLE_VSYNC_PROPERTY = "hwui.disable_vsync";

    /**
     * Turn on to draw dirty regions every other frame.
     */
@@ -303,6 +313,7 @@ public abstract class HardwareRenderer {

        boolean mDirtyRegions;
        final boolean mDirtyRegionsRequested;
        final boolean mVsyncDisabled;

        final int mGlVersion;
        final boolean mTranslucent;
@@ -314,10 +325,17 @@ public abstract class HardwareRenderer {
        GlRenderer(int glVersion, boolean translucent) {
            mGlVersion = glVersion;
            mTranslucent = translucent;

            final String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true");
            //noinspection PointlessBooleanExpression,ConstantConditions
            mDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty);
            mDirtyRegionsRequested = mDirtyRegions;

            final String vsyncProperty = SystemProperties.get(DISABLE_VSYNC_PROPERTY, "false");
            mVsyncDisabled = "true".equalsIgnoreCase(vsyncProperty);
            if (mVsyncDisabled) {
                Log.d(LOG_TAG, "Disabling v-sync");
            }
        }

        /**
@@ -764,6 +782,14 @@ public abstract class HardwareRenderer {
            }
        }

        @Override
        void setup(int width, int height) {
            super.setup(width, height);
            if (mVsyncDisabled) {
                GLES20Canvas.disableVsync();
            }
        }

        @Override
        DisplayList createDisplayList(View v) {
            return new GLES20DisplayList(v);
+18 −5
Original line number Diff line number Diff line
@@ -115,6 +115,18 @@ static jboolean android_view_GLES20Canvas_isBackBufferPreserved(JNIEnv* env, job
    return error == EGL_SUCCESS && value == EGL_BUFFER_PRESERVED;
}

static void android_view_GLES20Canvas_disableVsync(JNIEnv* env, jobject clazz) {
    EGLDisplay display = eglGetCurrentDisplay();

    eglGetError();
    eglSwapInterval(display, 0);

    EGLint error = eglGetError();
    if (error != EGL_SUCCESS) {
        RENDERER_LOGD("Could not disable v-sync (%x)", error);
    }
}

// ----------------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------------
@@ -621,7 +633,7 @@ static Layer* android_view_GLES20Canvas_createTextureLayer(JNIEnv* env, jobject

    if (layer) {
        jint* storage = env->GetIntArrayElements(layerInfo, NULL);
        storage[0] = layer->texture;
        storage[0] = layer->getTexture();
        env->ReleaseIntArrayElements(layerInfo, storage, 0);
    }

@@ -634,8 +646,8 @@ static Layer* android_view_GLES20Canvas_createLayer(JNIEnv* env, jobject clazz,

    if (layer) {
        jint* storage = env->GetIntArrayElements(layerInfo, NULL);
        storage[0] = layer->width;
        storage[1] = layer->height;
        storage[0] = layer->getWidth();
        storage[1] = layer->getHeight();
        env->ReleaseIntArrayElements(layerInfo, storage, 0);
    }

@@ -647,8 +659,8 @@ static void android_view_GLES20Canvas_resizeLayer(JNIEnv* env, jobject clazz,
    LayerRenderer::resizeLayer(layer, width, height);

    jint* storage = env->GetIntArrayElements(layerInfo, NULL);
    storage[0] = layer->width;
    storage[1] = layer->height;
    storage[0] = layer->getWidth();
    storage[1] = layer->getHeight();
    env->ReleaseIntArrayElements(layerInfo, storage, 0);
}

@@ -722,6 +734,7 @@ static JNINativeMethod gMethods[] = {
#ifdef USE_OPENGL_RENDERER
    { "nIsBackBufferPreserved", "()Z",         (void*) android_view_GLES20Canvas_isBackBufferPreserved },
    { "nPreserveBackBuffer",    "()Z",         (void*) android_view_GLES20Canvas_preserveBackBuffer },
    { "nDisableVsync",          "()V",         (void*) android_view_GLES20Canvas_disableVsync },

    { "nCreateRenderer",    "()I",             (void*) android_view_GLES20Canvas_createRenderer },
    { "nDestroyRenderer",   "(I)V",            (void*) android_view_GLES20Canvas_destroyRenderer },
+184 −47
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@

#include "Rect.h"
#include "SkiaColorFilter.h"
#include "Texture.h"
#include "Vertex.h"

namespace android {
@@ -40,14 +41,18 @@ namespace uirenderer {
 * A layer has dimensions and is backed by an OpenGL texture or FBO.
 */
struct Layer {
    Layer(const uint32_t layerWidth, const uint32_t layerHeight):
            width(layerWidth), height(layerHeight) {
    Layer(const uint32_t layerWidth, const uint32_t layerHeight) {
        mesh = NULL;
        meshIndices = NULL;
        meshElementCount = 0;
        isCacheable = true;
        isTextureLayer = false;
        cacheable = true;
        textureLayer = false;
        renderTarget = GL_TEXTURE_2D;
        texture.width = layerWidth;
        texture.height = layerHeight;
        colorFilter = NULL;
        firstFilter = true;
        firstWrap = true;
    }

    ~Layer() {
@@ -64,12 +69,152 @@ struct Layer {
        regionRect.set(bounds.leftTop().x, bounds.leftTop().y,
               bounds.rightBottom().x, bounds.rightBottom().y);

        const float texX = 1.0f / float(width);
        const float texY = 1.0f / float(height);
        const float texX = 1.0f / float(texture.width);
        const float texY = 1.0f / float(texture.height);
        const float height = layer.getHeight();
        texCoords.set(
               regionRect.left * texX, (height - regionRect.top) * texY,
               regionRect.right * texX, (height - regionRect.bottom) * texY);

        regionRect.translate(layer.left, layer.top);
    }

    inline uint32_t getWidth() {
        return texture.width;
    }

    inline uint32_t getHeight() {
        return texture.height;
    }

    void setSize(uint32_t width, uint32_t height) {
        texture.width = width;
        texture.height = height;
    }

    inline void setBlend(bool blend) {
        texture.blend = blend;
    }

    inline bool isBlend() {
        return texture.blend;
    }

    inline void setAlpha(int alpha) {
        this->alpha = alpha;
    }

    inline void setAlpha(int alpha, SkXfermode::Mode mode) {
        this->alpha = alpha;
        this->mode = mode;
    }

    inline int getAlpha() {
        return alpha;
    }

    inline SkXfermode::Mode getMode() {
        return mode;
    }

    inline void setEmpty(bool empty) {
        this->empty = empty;
    }

    inline bool isEmpty() {
        return empty;
    }

    inline void setFbo(GLuint fbo) {
        this->fbo = fbo;
    }

    inline GLuint getFbo() {
        return fbo;
    }

    inline GLuint* getTexturePointer() {
        return &texture.id;
    }

    inline GLuint getTexture() {
        return texture.id;
    }

    inline GLenum getRenderTarget() {
        return renderTarget;
    }

    inline void setRenderTarget(GLenum renderTarget) {
        this->renderTarget = renderTarget;
    }

    void setWrap(GLenum wrapS, GLenum wrapT, bool bindTexture = false, bool force = false) {
        if (firstWrap || force || wrapS != texture.wrapS || wrapT != texture.wrapT) {
            firstWrap = true;
            texture.setWrap(wrapS, wrapT);
            if (bindTexture) {
                glBindTexture(renderTarget, texture.id);
            }
            glTexParameteri(renderTarget, GL_TEXTURE_WRAP_S, wrapS);
            glTexParameteri(renderTarget, GL_TEXTURE_WRAP_T, wrapT);
        }
    }

    void setFilter(GLenum min, GLenum mag, bool bindTexture = false, bool force = false) {
        if (firstFilter || force || min != texture.minFilter || mag != texture.magFilter) {
            firstFilter = false;
            texture.setFilter(min, mag);
            if (bindTexture) {
                glBindTexture(renderTarget, texture.id);
            }
            glTexParameteri(renderTarget, GL_TEXTURE_MIN_FILTER, min);
            glTexParameteri(renderTarget, GL_TEXTURE_MAG_FILTER, mag);
        }
    }

    inline bool isCacheable() {
        return cacheable;
    }

    inline void setCacheable(bool cacheable) {
        this->cacheable = cacheable;
    }

    inline bool isTextureLayer() {
        return textureLayer;
    }

    inline void setTextureLayer(bool textureLayer) {
        this->textureLayer = textureLayer;
    }

    inline SkiaColorFilter* getColorFilter() {
        return colorFilter;
    }

    inline void setColorFilter(SkiaColorFilter* filter) {
        colorFilter = filter;
    }

    inline void bindTexture() {
        glBindTexture(renderTarget, texture.id);
    }

    inline void generateTexture() {
        glGenTextures(1, &texture.id);
    }

    inline void deleteTexture() {
        if (texture.id) glDeleteTextures(1, &texture.id);
    }

    inline void allocateTexture(GLenum format, GLenum storage) {
        glTexImage2D(renderTarget, 0, format, getWidth(), getHeight(), 0, format, storage, NULL);
    }

    inline mat4& getTexTransform() {
        return texTransform;
    }

    /**
@@ -82,23 +227,29 @@ struct Layer {
    Rect texCoords;

    /**
     * Name of the FBO used to render the layer. If the name is 0
     * this layer is not backed by an FBO, but a simple texture.
     * Dirty region indicating what parts of the layer
     * have been drawn.
     */
    GLuint fbo;

    Region region;
    /**
     * Opacity of the layer.
     * If the region is a rectangle, coordinates of the
     * region are stored here.
     */
    int alpha;
    Rect regionRect;

    /**
     * Blending mode of the layer.
     * If the layer can be rendered as a mesh, this is non-null.
     */
    SkXfermode::Mode mode;
    TextureVertex* mesh;
    uint16_t* meshIndices;
    GLsizei meshElementCount;

private:
    /**
     * Indicates whether this layer should be blended.
     * Name of the FBO used to render the layer. If the name is 0
     * this layer is not backed by an FBO, but a simple texture.
     */
    bool blend;
    GLuint fbo;

    /**
     * Indicates whether this layer has been used already.
@@ -106,28 +257,25 @@ struct Layer {
    bool empty;

    /**
     * Name of the texture used to render the layer.
     * The texture backing this layer.
     */
    GLuint texture;
    /**
     * Width of the layer texture.
     */
    uint32_t width;
    Texture texture;

    /**
     * Height of the layer texture.
     * If set to true (by default), the layer can be reused.
     */
    uint32_t height;
    bool cacheable;

    /**
     * Dirty region indicating what parts of the layer
     * have been drawn.
     * When set to true, this layer must be treated as a texture
     * layer.
     */
    Region region;
    bool textureLayer;

    /**
     * If the region is a rectangle, coordinates of the
     * region are stored here.
     * Indicates the render target.
     */
    Rect regionRect;
    GLenum renderTarget;

    /**
     * Color filter used to draw this layer. Optional.
@@ -135,32 +283,21 @@ struct Layer {
    SkiaColorFilter* colorFilter;

    /**
     * If the layer can be rendered as a mesh, this is non-null.
     */
    TextureVertex* mesh;
    uint16_t* meshIndices;
    GLsizei meshElementCount;

    /**
     * If set to true (by default), the layer can be reused.
     * Opacity of the layer.
     */
    bool isCacheable;

    int alpha;
    /**
     * When set to true, this layer must be treated as a texture
     * layer.
     * Blending mode of the layer.
     */
    bool isTextureLayer;
    SkXfermode::Mode mode;

    /**
     * Optional texture coordinates transform.
     */
    mat4 texTransform;

    /**
     * Indicates the render target.
     */
    GLenum renderTarget;
    bool firstFilter;
    bool firstWrap;
}; // struct Layer

}; // namespace uirenderer
+22 −28
Original line number Diff line number Diff line
@@ -68,8 +68,8 @@ void LayerCache::setMaxSize(uint32_t maxSize) {

void LayerCache::deleteLayer(Layer* layer) {
    if (layer) {
        mSize -= layer->width * layer->height * 4;
        glDeleteTextures(1, &layer->texture);
        mSize -= layer->getWidth() * layer->getHeight() * 4;
        layer->deleteTexture();
        delete layer;
    }
}
@@ -93,29 +93,23 @@ Layer* LayerCache::get(const uint32_t width, const uint32_t height) {
        mCache.removeAt(index);

        layer = entry.mLayer;
        mSize -= layer->width * layer->height * 4;
        mSize -= layer->getWidth() * layer->getHeight() * 4;

        LAYER_LOGD("Reusing layer %dx%d", layer->width, layer->height);
        LAYER_LOGD("Reusing layer %dx%d", layer->getWidth(), layer->getHeight());
    } else {
        LAYER_LOGD("Creating new layer %dx%d", entry.mWidth, entry.mHeight);

        layer = new Layer(entry.mWidth, entry.mHeight);
        layer->blend = true;
        layer->empty = true;
        layer->fbo = 0;
        layer->colorFilter = NULL;

        glGenTextures(1, &layer->texture);
        glBindTexture(GL_TEXTURE_2D, layer->texture);

        layer->setBlend(true);
        layer->setEmpty(true);
        layer->setFbo(0);

        layer->generateTexture();
        layer->bindTexture();
        layer->setFilter(GL_NEAREST, GL_NEAREST);
        layer->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

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

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

#if DEBUG_LAYERS
        size_t size = mCache.size();
        for (size_t i = 0; i < size; i++) {
@@ -133,30 +127,30 @@ bool LayerCache::resize(Layer* layer, const uint32_t width, const uint32_t heigh
    //       size already in the cache, and reuse it instead of creating a new one

    LayerEntry entry(width, height);
    if (entry.mWidth <= layer->width && entry.mHeight <= layer->height) {
    if (entry.mWidth <= layer->getWidth() && entry.mHeight <= layer->getHeight()) {
        return true;
    }

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, layer->texture);
    uint32_t oldWidth = layer->getWidth();
    uint32_t oldHeight = layer->getHeight();

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, entry.mWidth, entry.mHeight, 0,
            GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    glActiveTexture(GL_TEXTURE0);
    layer->bindTexture();
    layer->setSize(entry.mWidth, entry.mHeight);
    layer->allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE);

    if (glGetError() != GL_NO_ERROR) {
        layer->setSize(oldWidth, oldHeight);
        return false;
    }

    layer->width = entry.mWidth;
    layer->height = entry.mHeight;

    return true;
}

bool LayerCache::put(Layer* layer) {
    if (!layer->isCacheable) return false;
    if (!layer->isCacheable()) return false;

    const uint32_t size = layer->width * layer->height * 4;
    const uint32_t size = layer->getWidth() * layer->getHeight() * 4;
    // Don't even try to cache a layer that's bigger than the cache
    if (size < mMaxSize) {
        // TODO: Use an LRU
Loading