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

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

Merge "Use NEAREST filtering for layers whenever possible."

parents 4a6d113b 9ace8f5e
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