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

Commit 382e17d4 authored by Mathias Agopian's avatar Mathias Agopian
Browse files

fix [2211532] improves sholes graphics performance

Instead of using glTex{Sub}Image2D() to refresh the textures, we're using an EGLImageKHR object
backed up by a gralloc buffer. The data is updated using memcpy(). This is faster than
glTex{Sub}Image2D() because the texture is not swizzled. It also uses less memory because
EGLImageKHW is not limited to power-of-two dimensions.
parent dd28d563
Loading
Loading
Loading
Loading
+142 −63
Original line number Diff line number Diff line
@@ -130,30 +130,18 @@ status_t Layer::setBuffers( uint32_t w, uint32_t h,
    return NO_ERROR;
}

void Layer::reloadTexture(const Region& dirty)
status_t Layer::initializeEglImageLocked(
        const sp<GraphicBuffer>& buffer, Texture* texture)
{
    Mutex::Autolock _l(mLock);
    sp<GraphicBuffer> buffer(getFrontBufferLocked());
    if (LIKELY((mFlags & DisplayHardware::DIRECT_TEXTURE) &&
            (buffer->usage & GRALLOC_USAGE_HW_TEXTURE))) {
        int index = mFrontBufferIndex;
        if (LIKELY(!mTextures[index].dirty)) {
            glBindTexture(GL_TEXTURE_2D, mTextures[index].name);
        } else {
    status_t err = NO_ERROR;

    // we need to recreate the texture
    EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());

            // create the new texture name if needed
            if (UNLIKELY(mTextures[index].name == -1U)) {
                mTextures[index].name = createTexture();
            } else {
                glBindTexture(GL_TEXTURE_2D, mTextures[index].name);
            }

    // free the previous image
            if (mTextures[index].image != EGL_NO_IMAGE_KHR) {
                eglDestroyImageKHR(dpy, mTextures[index].image);
                mTextures[index].image = EGL_NO_IMAGE_KHR;
    if (texture->image != EGL_NO_IMAGE_KHR) {
        eglDestroyImageKHR(dpy, texture->image);
        texture->image = EGL_NO_IMAGE_KHR;
    }

    // construct an EGL_NATIVE_BUFFER_ANDROID
@@ -164,57 +152,148 @@ void Layer::reloadTexture(const Region& dirty)
            EGL_IMAGE_PRESERVED_KHR,    EGL_TRUE,
            EGL_NONE,                   EGL_NONE
    };
            mTextures[index].image = eglCreateImageKHR(
    texture->image = eglCreateImageKHR(
            dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
            (EGLClientBuffer)clientBuf, attrs);

            LOGE_IF(mTextures[index].image == EGL_NO_IMAGE_KHR,
    LOGE_IF(texture->image == EGL_NO_IMAGE_KHR,
            "eglCreateImageKHR() failed. err=0x%4x",
            eglGetError());

            if (mTextures[index].image != EGL_NO_IMAGE_KHR) {
    if (texture->image != EGL_NO_IMAGE_KHR) {
        glBindTexture(GL_TEXTURE_2D, texture->name);
        glEGLImageTargetTexture2DOES(GL_TEXTURE_2D,
                        (GLeglImageOES)mTextures[index].image);
                (GLeglImageOES)texture->image);
        GLint error = glGetError();
        if (UNLIKELY(error != GL_NO_ERROR)) {
                    // this failed, for instance, because we don't support
                    // NPOT.
            // this failed, for instance, because we don't support NPOT.
            // FIXME: do something!
                    LOGD("layer=%p, glEGLImageTargetTexture2DOES(%p) "
            LOGE("layer=%p, glEGLImageTargetTexture2DOES(%p) "
                 "failed err=0x%04x",
                         this, mTextures[index].image, error);
                 this, texture->image, error);
            mFlags &= ~DisplayHardware::DIRECT_TEXTURE;
            err = INVALID_OPERATION;
        } else {
            // Everything went okay!
                    mTextures[index].NPOTAdjust = false;
                    mTextures[index].dirty  = false;
                    mTextures[index].width  = clientBuf->width;
                    mTextures[index].height = clientBuf->height;
            texture->NPOTAdjust = false;
            texture->dirty  = false;
            texture->width  = clientBuf->width;
            texture->height = clientBuf->height;
        }
    } else {
        err = INVALID_OPERATION;
    }
    return err;
}

void Layer::reloadTexture(const Region& dirty)
{
    Mutex::Autolock _l(mLock);
    sp<GraphicBuffer> buffer(getFrontBufferLocked());
    int index = mFrontBufferIndex;

    // create the new texture name if needed
    if (UNLIKELY(mTextures[index].name == -1U)) {
        mTextures[index].name = createTexture();
        mTextures[index].width = 0;
        mTextures[index].height = 0;
    }

    if (mFlags & DisplayHardware::DIRECT_TEXTURE) {
        if (buffer->usage & GraphicBuffer::USAGE_HW_TEXTURE) {
            if (mTextures[index].dirty) {
                initializeEglImageLocked(buffer, &mTextures[index]);
            }
        } else {
        for (size_t i=0 ; i<NUM_BUFFERS ; i++)
            mTextures[i].image = EGL_NO_IMAGE_KHR;
            if (mHybridBuffer==0 || (mHybridBuffer->width != buffer->width ||
                    mHybridBuffer->height != buffer->height)) {
                mHybridBuffer.clear();
                mHybridBuffer = new GraphicBuffer(
                        buffer->width, buffer->height, buffer->format,
                        GraphicBuffer::USAGE_SW_WRITE_OFTEN |
                        GraphicBuffer::USAGE_HW_TEXTURE);
                initializeEglImageLocked(
                        mHybridBuffer, &mTextures[0]);
            }

            GGLSurface t;
            status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN);
            LOGE_IF(res, "error %d (%s) locking buffer %p",
                    res, strerror(res), buffer.get());
            if (res == NO_ERROR) {
                Texture* const texture(&mTextures[0]);

                glBindTexture(GL_TEXTURE_2D, texture->name);

                sp<GraphicBuffer> buf(mHybridBuffer);
                void* vaddr;
                res = buf->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, &vaddr);
                if (res == NO_ERROR) {
            if (UNLIKELY(mTextures[0].name == -1U)) {
                mTextures[0].name = createTexture();
                mTextures[0].width = 0;
                mTextures[0].height = 0;
                    int bpp = 0;
                    switch (t.format) {
                    case GGL_PIXEL_FORMAT_RGB_565:
                    case GGL_PIXEL_FORMAT_RGBA_4444:
                        bpp = 2;
                        break;
                    case GGL_PIXEL_FORMAT_RGBA_8888:
                    case GGL_PIXEL_FORMAT_RGBX_8888:
                        bpp = 4;
                        break;
                    case GGL_PIXEL_FORMAT_YCbCr_422_SP:
                    case GGL_PIXEL_FORMAT_YCbCr_420_SP:
                        // just show the Y plane of YUV buffers
                        bpp = 1;
                        break;
                    default:
                        // oops, we don't handle this format!
                        LOGE("layer %p, texture=%d, using format %d, which is not "
                                "supported by the GL", this, texture->name, t.format);
                    }
                    if (bpp) {
                        const Rect bounds(dirty.getBounds());
                        size_t src_stride = t.stride;
                        size_t dst_stride = buf->stride;
                        if (src_stride == dst_stride &&
                            bounds.width() == t.width &&
                            bounds.height() == t.height)
                        {
                            memcpy(vaddr, t.data, t.height * t.stride * bpp);
                        } else {
                            GLubyte const * src = t.data +
                                (bounds.left + bounds.top * src_stride) * bpp;
                            GLubyte * dst = (GLubyte *)vaddr +
                                (bounds.left + bounds.top * dst_stride) * bpp;
                            const size_t length = bounds.width() * bpp;
                            size_t h = bounds.height();
                            src_stride *= bpp;
                            dst_stride *= bpp;
                            while (h--) {
                                memcpy(dst, src, length);
                                dst += dst_stride;
                                src += src_stride;
                            }
                        }
                    }
                    buf->unlock();
                }
                buffer->unlock();
            }
        }
    } else {
        for (size_t i=0 ; i<NUM_BUFFERS ; i++) {
            mTextures[i].image = EGL_NO_IMAGE_KHR;
        }
        GGLSurface t;
        status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN);
        LOGE_IF(res, "error %d (%s) locking buffer %p",
                res, strerror(res), buffer.get());
        if (res == NO_ERROR) {
            loadTexture(&mTextures[0], dirty, t);
            buffer->unlock();
        }
    }
}


void Layer::onDraw(const Region& clip) const
{
    int index = mFrontBufferIndex;
+3 −0
Original line number Diff line number Diff line
@@ -85,6 +85,8 @@ private:
    }
 
    void reloadTexture(const Region& dirty);
    status_t initializeEglImageLocked(
            const sp<GraphicBuffer>& buffer, Texture* texture);

    uint32_t getEffectiveUsage(uint32_t usage) const;

@@ -118,6 +120,7 @@ private:
            // protected by mLock
            sp<GraphicBuffer> mBuffers[NUM_BUFFERS];
            Texture         mTextures[NUM_BUFFERS];
            sp<GraphicBuffer> mHybridBuffer;
            uint32_t        mWidth;
            uint32_t        mHeight;
            
+1 −1

File changed.

Contains only whitespace changes.