diff options
Diffstat (limited to 'services/surfaceflinger/TextureManager.cpp')
| -rw-r--r-- | services/surfaceflinger/TextureManager.cpp | 341 | 
1 files changed, 341 insertions, 0 deletions
| diff --git a/services/surfaceflinger/TextureManager.cpp b/services/surfaceflinger/TextureManager.cpp new file mode 100644 index 000000000000..76f61598b15e --- /dev/null +++ b/services/surfaceflinger/TextureManager.cpp @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/Log.h> + +#include <ui/GraphicBuffer.h> + +#include <GLES/gl.h> +#include <GLES/glext.h> + +#include <hardware/hardware.h> + +#include "clz.h" +#include "DisplayHardware/DisplayHardware.h" +#include "GLExtensions.h" +#include "TextureManager.h" + +namespace android { + +// --------------------------------------------------------------------------- + +TextureManager::TextureManager() +    : mGLExtensions(GLExtensions::getInstance()) +{ +} + +GLenum TextureManager::getTextureTarget(const Image* image) { +#if defined(GL_OES_texture_external) +    switch (image->target) { +        case Texture::TEXTURE_EXTERNAL: +            return GL_TEXTURE_EXTERNAL_OES; +    } +#endif +    return GL_TEXTURE_2D; +} + +status_t TextureManager::initTexture(Texture* texture) +{ +    if (texture->name != -1UL) +        return INVALID_OPERATION; + +    GLuint textureName = -1; +    glGenTextures(1, &textureName); +    texture->name = textureName; +    texture->width = 0; +    texture->height = 0; + +    const GLenum target = GL_TEXTURE_2D; +    glBindTexture(target, textureName); +    glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); +    glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +    glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +    glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + +    return NO_ERROR; +} + +status_t TextureManager::initTexture(Image* pImage, int32_t format) +{ +    if (pImage->name != -1UL) +        return INVALID_OPERATION; + +    GLuint textureName = -1; +    glGenTextures(1, &textureName); +    pImage->name = textureName; +    pImage->width = 0; +    pImage->height = 0; + +    GLenum target = GL_TEXTURE_2D; +#if defined(GL_OES_texture_external) +    if (GLExtensions::getInstance().haveTextureExternal()) { +        if (format && isYuvFormat(format)) { +            target = GL_TEXTURE_EXTERNAL_OES; +            pImage->target = Texture::TEXTURE_EXTERNAL; +        } +    } +#endif + +    glBindTexture(target, textureName); +    glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); +    glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +    glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +    glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + +    return NO_ERROR; +} + +bool TextureManager::isSupportedYuvFormat(int format) +{ +    switch (format) { +    case HAL_PIXEL_FORMAT_YV12: +        return true; +    } +    return false; +} + +bool TextureManager::isYuvFormat(int format) +{ +    switch (format) { +    // supported YUV formats +    case HAL_PIXEL_FORMAT_YV12: +    // Legacy/deprecated YUV formats +    case HAL_PIXEL_FORMAT_YCbCr_422_SP: +    case HAL_PIXEL_FORMAT_YCrCb_420_SP: +    case HAL_PIXEL_FORMAT_YCbCr_422_I: +        return true; +    } + +    // Any OEM format needs to be considered +    if (format>=0x100 && format<=0x1FF) +        return true; + +    return false; +} + +status_t TextureManager::initEglImage(Image* pImage, +        EGLDisplay dpy, const sp<GraphicBuffer>& buffer) +{ +    status_t err = NO_ERROR; +    if (!pImage->dirty) return err; + +    // free the previous image +    if (pImage->image != EGL_NO_IMAGE_KHR) { +        eglDestroyImageKHR(dpy, pImage->image); +        pImage->image = EGL_NO_IMAGE_KHR; +    } + +    // construct an EGL_NATIVE_BUFFER_ANDROID +    android_native_buffer_t* clientBuf = buffer->getNativeBuffer(); + +    // create the new EGLImageKHR +    const EGLint attrs[] = { +            EGL_IMAGE_PRESERVED_KHR,    EGL_TRUE, +            EGL_NONE,                   EGL_NONE +    }; +    pImage->image = eglCreateImageKHR( +            dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, +            (EGLClientBuffer)clientBuf, attrs); + +    if (pImage->image != EGL_NO_IMAGE_KHR) { +        if (pImage->name == -1UL) { +            initTexture(pImage, buffer->format); +        } +        const GLenum target = getTextureTarget(pImage); +        glBindTexture(target, pImage->name); +        glEGLImageTargetTexture2DOES(target, (GLeglImageOES)pImage->image); +        GLint error = glGetError(); +        if (error != GL_NO_ERROR) { +            LOGE("glEGLImageTargetTexture2DOES(%p) failed err=0x%04x", +                    pImage->image, error); +            err = INVALID_OPERATION; +        } else { +            // Everything went okay! +            pImage->dirty  = false; +            pImage->width  = clientBuf->width; +            pImage->height = clientBuf->height; +        } +    } else { +        LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError()); +        err = INVALID_OPERATION; +    } +    return err; +} + +status_t TextureManager::loadTexture(Texture* texture, +        const Region& dirty, const GGLSurface& t) +{ +    if (texture->name == -1UL) { +        status_t err = initTexture(texture); +        LOGE_IF(err, "loadTexture failed in initTexture (%s)", strerror(err)); +        return err; +    } + +    if (texture->target != Texture::TEXTURE_2D) +        return INVALID_OPERATION; + +    glBindTexture(GL_TEXTURE_2D, texture->name); + +    /* +     * In OpenGL ES we can't specify a stride with glTexImage2D (however, +     * GL_UNPACK_ALIGNMENT is a limited form of stride). +     * So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we +     * need to do something reasonable (here creating a bigger texture). +     * +     * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT); +     * +     * This situation doesn't happen often, but some h/w have a limitation +     * for their framebuffer (eg: must be multiple of 8 pixels), and +     * we need to take that into account when using these buffers as +     * textures. +     * +     * This should never be a problem with POT textures +     */ + +    int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format)); +    unpack = 1 << ((unpack > 3) ? 3 : unpack); +    glPixelStorei(GL_UNPACK_ALIGNMENT, unpack); + +    /* +     * round to POT if needed +     */ +    if (!mGLExtensions.haveNpot()) { +        texture->NPOTAdjust = true; +    } + +    if (texture->NPOTAdjust) { +        // find the smallest power-of-two that will accommodate our surface +        texture->potWidth  = 1 << (31 - clz(t.width)); +        texture->potHeight = 1 << (31 - clz(t.height)); +        if (texture->potWidth  < t.width)  texture->potWidth  <<= 1; +        if (texture->potHeight < t.height) texture->potHeight <<= 1; +        texture->wScale = float(t.width)  / texture->potWidth; +        texture->hScale = float(t.height) / texture->potHeight; +    } else { +        texture->potWidth  = t.width; +        texture->potHeight = t.height; +    } + +    Rect bounds(dirty.bounds()); +    GLvoid* data = 0; +    if (texture->width != t.width || texture->height != t.height) { +        texture->width  = t.width; +        texture->height = t.height; + +        // texture size changed, we need to create a new one +        bounds.set(Rect(t.width, t.height)); +        if (t.width  == texture->potWidth && +            t.height == texture->potHeight) { +            // we can do it one pass +            data = t.data; +        } + +        if (t.format == HAL_PIXEL_FORMAT_RGB_565) { +            glTexImage2D(GL_TEXTURE_2D, 0, +                    GL_RGB, texture->potWidth, texture->potHeight, 0, +                    GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); +        } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) { +            glTexImage2D(GL_TEXTURE_2D, 0, +                    GL_RGBA, texture->potWidth, texture->potHeight, 0, +                    GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); +        } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 || +                   t.format == HAL_PIXEL_FORMAT_RGBX_8888) { +            glTexImage2D(GL_TEXTURE_2D, 0, +                    GL_RGBA, texture->potWidth, texture->potHeight, 0, +                    GL_RGBA, GL_UNSIGNED_BYTE, data); +        } else if (isSupportedYuvFormat(t.format)) { +            // just show the Y plane of YUV buffers +            glTexImage2D(GL_TEXTURE_2D, 0, +                    GL_LUMINANCE, texture->potWidth, texture->potHeight, 0, +                    GL_LUMINANCE, GL_UNSIGNED_BYTE, data); +        } else { +            // oops, we don't handle this format! +            LOGE("texture=%d, using format %d, which is not " +                 "supported by the GL", texture->name, t.format); +        } +    } +    if (!data) { +        if (t.format == HAL_PIXEL_FORMAT_RGB_565) { +            glTexSubImage2D(GL_TEXTURE_2D, 0, +                    0, bounds.top, t.width, bounds.height(), +                    GL_RGB, GL_UNSIGNED_SHORT_5_6_5, +                    t.data + bounds.top*t.stride*2); +        } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) { +            glTexSubImage2D(GL_TEXTURE_2D, 0, +                    0, bounds.top, t.width, bounds.height(), +                    GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, +                    t.data + bounds.top*t.stride*2); +        } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 || +                   t.format == HAL_PIXEL_FORMAT_RGBX_8888) { +            glTexSubImage2D(GL_TEXTURE_2D, 0, +                    0, bounds.top, t.width, bounds.height(), +                    GL_RGBA, GL_UNSIGNED_BYTE, +                    t.data + bounds.top*t.stride*4); +        } else if (isSupportedYuvFormat(t.format)) { +            // just show the Y plane of YUV buffers +            glTexSubImage2D(GL_TEXTURE_2D, 0, +                    0, bounds.top, t.width, bounds.height(), +                    GL_LUMINANCE, GL_UNSIGNED_BYTE, +                    t.data + bounds.top*t.stride); +        } +    } +    return NO_ERROR; +} + +void TextureManager::activateTexture(const Texture& texture, bool filter) +{ +    const GLenum target = getTextureTarget(&texture); +    if (target == GL_TEXTURE_2D) { +        glBindTexture(GL_TEXTURE_2D, texture.name); +        glEnable(GL_TEXTURE_2D); +#if defined(GL_OES_texture_external) +        if (GLExtensions::getInstance().haveTextureExternal()) { +            glDisable(GL_TEXTURE_EXTERNAL_OES); +        } +    } else { +        glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture.name); +        glEnable(GL_TEXTURE_EXTERNAL_OES); +        glDisable(GL_TEXTURE_2D); +#endif +    } + +    if (filter) { +        glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +        glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +    } else { +        glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +        glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); +    } +} + +void TextureManager::deactivateTextures() +{ +    glDisable(GL_TEXTURE_2D); +#if defined(GL_OES_texture_external) +    if (GLExtensions::getInstance().haveTextureExternal()) { +        glDisable(GL_TEXTURE_EXTERNAL_OES); +    } +#endif +} + +// --------------------------------------------------------------------------- + +}; // namespace android | 
