diff options
Diffstat (limited to 'libs/surfaceflinger/TextureManager.cpp')
| -rw-r--r-- | libs/surfaceflinger/TextureManager.cpp | 241 | 
1 files changed, 241 insertions, 0 deletions
diff --git a/libs/surfaceflinger/TextureManager.cpp b/libs/surfaceflinger/TextureManager.cpp new file mode 100644 index 000000000000..ee2159b3ffed --- /dev/null +++ b/libs/surfaceflinger/TextureManager.cpp @@ -0,0 +1,241 @@ +/* + * 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 "TextureManager.h" + +namespace android { + +// --------------------------------------------------------------------------- + +TextureManager::TextureManager(uint32_t flags) +    : mFlags(flags) +{ +} + +GLuint TextureManager::createTexture() +{ +    GLuint textureName = -1; +    glGenTextures(1, &textureName); +    glBindTexture(GL_TEXTURE_2D, textureName); +    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); +    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); +    return textureName; +} + +bool TextureManager::isSupportedYuvFormat(int format) +{ +    switch (format) { +        case HAL_PIXEL_FORMAT_YCbCr_422_SP: +        case HAL_PIXEL_FORMAT_YCbCr_420_SP: +        case HAL_PIXEL_FORMAT_YCbCr_422_P: +        case HAL_PIXEL_FORMAT_YCbCr_420_P: +        case HAL_PIXEL_FORMAT_YCbCr_422_I: +        case HAL_PIXEL_FORMAT_YCbCr_420_I: +        case HAL_PIXEL_FORMAT_YCrCb_420_SP: +            return true; +    } +    return false; +} + +status_t TextureManager::initEglImage(Image* texture, +        EGLDisplay dpy, const sp<GraphicBuffer>& buffer) +{ +    status_t err = NO_ERROR; +    if (!texture->dirty) return err; + +    // free the previous image +    if (texture->image != EGL_NO_IMAGE_KHR) { +        eglDestroyImageKHR(dpy, texture->image); +        texture->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 +    }; +    texture->image = eglCreateImageKHR( +            dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, +            (EGLClientBuffer)clientBuf, attrs); + +    if (texture->image != EGL_NO_IMAGE_KHR) { +        if (texture->name == -1UL) { +            texture->name = createTexture(); +            texture->width = 0; +            texture->height = 0; +        } +        glBindTexture(GL_TEXTURE_2D, texture->name); +        glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, +                (GLeglImageOES)texture->image); +        GLint error = glGetError(); +        if (error != GL_NO_ERROR) { +            LOGE("glEGLImageTargetTexture2DOES(%p) failed err=0x%04x", +                    texture->image, error); +            err = INVALID_OPERATION; +        } else { +            // Everything went okay! +            texture->dirty  = false; +            texture->width  = clientBuf->width; +            texture->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) { +        texture->name = createTexture(); +        texture->width = 0; +        texture->height = 0; +    } + +    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 (!(mFlags & DisplayHardware::NPOT_EXTENSION)) { +        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; +} + +// --------------------------------------------------------------------------- + +}; // namespace android  |