Home | History | Annotate | Download | only in surfaceflinger
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <stdlib.h>
     18 #include <stdint.h>
     19 #include <sys/types.h>
     20 
     21 #include <utils/Errors.h>
     22 #include <utils/Log.h>
     23 
     24 #include <ui/GraphicBuffer.h>
     25 
     26 #include <GLES/gl.h>
     27 #include <GLES/glext.h>
     28 
     29 #include <hardware/hardware.h>
     30 
     31 #include "clz.h"
     32 #include "DisplayHardware/DisplayHardware.h"
     33 #include "GLExtensions.h"
     34 #include "TextureManager.h"
     35 
     36 namespace android {
     37 
     38 // ---------------------------------------------------------------------------
     39 
     40 TextureManager::TextureManager()
     41     : mGLExtensions(GLExtensions::getInstance())
     42 {
     43 }
     44 
     45 GLenum TextureManager::getTextureTarget(const Image* image) {
     46 #if defined(GL_OES_EGL_image_external)
     47     switch (image->target) {
     48         case Texture::TEXTURE_EXTERNAL:
     49             return GL_TEXTURE_EXTERNAL_OES;
     50     }
     51 #endif
     52     return GL_TEXTURE_2D;
     53 }
     54 
     55 status_t TextureManager::initTexture(Texture* texture)
     56 {
     57     if (texture->name != -1UL)
     58         return INVALID_OPERATION;
     59 
     60     GLuint textureName = -1;
     61     glGenTextures(1, &textureName);
     62     texture->name = textureName;
     63     texture->width = 0;
     64     texture->height = 0;
     65 
     66     const GLenum target = GL_TEXTURE_2D;
     67     glBindTexture(target, textureName);
     68     glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
     69     glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
     70     glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     71     glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     72 
     73     return NO_ERROR;
     74 }
     75 
     76 status_t TextureManager::initTexture(Image* pImage, int32_t format)
     77 {
     78     if (pImage->name != -1UL)
     79         return INVALID_OPERATION;
     80 
     81     GLuint textureName = -1;
     82     glGenTextures(1, &textureName);
     83     pImage->name = textureName;
     84     pImage->width = 0;
     85     pImage->height = 0;
     86 
     87     GLenum target = GL_TEXTURE_2D;
     88 #if defined(GL_OES_EGL_image_external)
     89     if (GLExtensions::getInstance().haveTextureExternal()) {
     90         if (format && isYuvFormat(format)) {
     91             target = GL_TEXTURE_EXTERNAL_OES;
     92             pImage->target = Texture::TEXTURE_EXTERNAL;
     93         }
     94     }
     95 #endif
     96 
     97     glBindTexture(target, textureName);
     98     glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
     99     glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    100     glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    101     glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    102 
    103     return NO_ERROR;
    104 }
    105 
    106 bool TextureManager::isSupportedYuvFormat(int format)
    107 {
    108     switch (format) {
    109     case HAL_PIXEL_FORMAT_YV12:
    110         return true;
    111     }
    112     return false;
    113 }
    114 
    115 bool TextureManager::isYuvFormat(int format)
    116 {
    117     switch (format) {
    118     // supported YUV formats
    119     case HAL_PIXEL_FORMAT_YV12:
    120     // Legacy/deprecated YUV formats
    121     case HAL_PIXEL_FORMAT_YCbCr_422_SP:
    122     case HAL_PIXEL_FORMAT_YCrCb_420_SP:
    123     case HAL_PIXEL_FORMAT_YCbCr_422_I:
    124         return true;
    125     }
    126 
    127     // Any OEM format needs to be considered
    128     if (format>=0x100 && format<=0x1FF)
    129         return true;
    130 
    131     return false;
    132 }
    133 
    134 status_t TextureManager::initEglImage(Image* pImage,
    135         EGLDisplay dpy, const sp<GraphicBuffer>& buffer)
    136 {
    137     status_t err = NO_ERROR;
    138     if (!pImage->dirty) return err;
    139 
    140     // free the previous image
    141     if (pImage->image != EGL_NO_IMAGE_KHR) {
    142         eglDestroyImageKHR(dpy, pImage->image);
    143         pImage->image = EGL_NO_IMAGE_KHR;
    144     }
    145 
    146     // construct an EGL_NATIVE_BUFFER_ANDROID
    147     android_native_buffer_t* clientBuf = buffer->getNativeBuffer();
    148 
    149     // create the new EGLImageKHR
    150     const EGLint attrs[] = {
    151             EGL_IMAGE_PRESERVED_KHR,    EGL_TRUE,
    152             EGL_NONE,                   EGL_NONE
    153     };
    154     pImage->image = eglCreateImageKHR(
    155             dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
    156             (EGLClientBuffer)clientBuf, attrs);
    157 
    158     if (pImage->image != EGL_NO_IMAGE_KHR) {
    159         if (pImage->name == -1UL) {
    160             initTexture(pImage, buffer->format);
    161         }
    162         const GLenum target = getTextureTarget(pImage);
    163         glBindTexture(target, pImage->name);
    164         glEGLImageTargetTexture2DOES(target, (GLeglImageOES)pImage->image);
    165         GLint error = glGetError();
    166         if (error != GL_NO_ERROR) {
    167             LOGE("glEGLImageTargetTexture2DOES(%p) failed err=0x%04x",
    168                     pImage->image, error);
    169             err = INVALID_OPERATION;
    170         } else {
    171             // Everything went okay!
    172             pImage->dirty  = false;
    173             pImage->width  = clientBuf->width;
    174             pImage->height = clientBuf->height;
    175         }
    176     } else {
    177         LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError());
    178         err = INVALID_OPERATION;
    179     }
    180     return err;
    181 }
    182 
    183 status_t TextureManager::loadTexture(Texture* texture,
    184         const Region& dirty, const GGLSurface& t)
    185 {
    186     if (texture->name == -1UL) {
    187         status_t err = initTexture(texture);
    188         LOGE_IF(err, "loadTexture failed in initTexture (%s)", strerror(err));
    189         return err;
    190     }
    191 
    192     if (texture->target != Texture::TEXTURE_2D)
    193         return INVALID_OPERATION;
    194 
    195     glBindTexture(GL_TEXTURE_2D, texture->name);
    196 
    197     /*
    198      * In OpenGL ES we can't specify a stride with glTexImage2D (however,
    199      * GL_UNPACK_ALIGNMENT is a limited form of stride).
    200      * So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we
    201      * need to do something reasonable (here creating a bigger texture).
    202      *
    203      * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT);
    204      *
    205      * This situation doesn't happen often, but some h/w have a limitation
    206      * for their framebuffer (eg: must be multiple of 8 pixels), and
    207      * we need to take that into account when using these buffers as
    208      * textures.
    209      *
    210      * This should never be a problem with POT textures
    211      */
    212 
    213     int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format));
    214     unpack = 1 << ((unpack > 3) ? 3 : unpack);
    215     glPixelStorei(GL_UNPACK_ALIGNMENT, unpack);
    216 
    217     /*
    218      * round to POT if needed
    219      */
    220     if (!mGLExtensions.haveNpot()) {
    221         texture->NPOTAdjust = true;
    222     }
    223 
    224     if (texture->NPOTAdjust) {
    225         // find the smallest power-of-two that will accommodate our surface
    226         texture->potWidth  = 1 << (31 - clz(t.width));
    227         texture->potHeight = 1 << (31 - clz(t.height));
    228         if (texture->potWidth  < t.width)  texture->potWidth  <<= 1;
    229         if (texture->potHeight < t.height) texture->potHeight <<= 1;
    230         texture->wScale = float(t.width)  / texture->potWidth;
    231         texture->hScale = float(t.height) / texture->potHeight;
    232     } else {
    233         texture->potWidth  = t.width;
    234         texture->potHeight = t.height;
    235     }
    236 
    237     Rect bounds(dirty.bounds());
    238     GLvoid* data = 0;
    239     if (texture->width != t.width || texture->height != t.height) {
    240         texture->width  = t.width;
    241         texture->height = t.height;
    242 
    243         // texture size changed, we need to create a new one
    244         bounds.set(Rect(t.width, t.height));
    245         if (t.width  == texture->potWidth &&
    246             t.height == texture->potHeight) {
    247             // we can do it one pass
    248             data = t.data;
    249         }
    250 
    251         if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
    252             glTexImage2D(GL_TEXTURE_2D, 0,
    253                     GL_RGB, texture->potWidth, texture->potHeight, 0,
    254                     GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
    255         } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
    256             glTexImage2D(GL_TEXTURE_2D, 0,
    257                     GL_RGBA, texture->potWidth, texture->potHeight, 0,
    258                     GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data);
    259         } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
    260                    t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
    261             glTexImage2D(GL_TEXTURE_2D, 0,
    262                     GL_RGBA, texture->potWidth, texture->potHeight, 0,
    263                     GL_RGBA, GL_UNSIGNED_BYTE, data);
    264         } else if (isSupportedYuvFormat(t.format)) {
    265             // just show the Y plane of YUV buffers
    266             glTexImage2D(GL_TEXTURE_2D, 0,
    267                     GL_LUMINANCE, texture->potWidth, texture->potHeight, 0,
    268                     GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
    269         } else {
    270             // oops, we don't handle this format!
    271             LOGE("texture=%d, using format %d, which is not "
    272                  "supported by the GL", texture->name, t.format);
    273         }
    274     }
    275     if (!data) {
    276         if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
    277             glTexSubImage2D(GL_TEXTURE_2D, 0,
    278                     0, bounds.top, t.width, bounds.height(),
    279                     GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
    280                     t.data + bounds.top*t.stride*2);
    281         } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
    282             glTexSubImage2D(GL_TEXTURE_2D, 0,
    283                     0, bounds.top, t.width, bounds.height(),
    284                     GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4,
    285                     t.data + bounds.top*t.stride*2);
    286         } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
    287                    t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
    288             glTexSubImage2D(GL_TEXTURE_2D, 0,
    289                     0, bounds.top, t.width, bounds.height(),
    290                     GL_RGBA, GL_UNSIGNED_BYTE,
    291                     t.data + bounds.top*t.stride*4);
    292         } else if (isSupportedYuvFormat(t.format)) {
    293             // just show the Y plane of YUV buffers
    294             glTexSubImage2D(GL_TEXTURE_2D, 0,
    295                     0, bounds.top, t.width, bounds.height(),
    296                     GL_LUMINANCE, GL_UNSIGNED_BYTE,
    297                     t.data + bounds.top*t.stride);
    298         }
    299     }
    300     return NO_ERROR;
    301 }
    302 
    303 void TextureManager::activateTexture(const Texture& texture, bool filter)
    304 {
    305     const GLenum target = getTextureTarget(&texture);
    306     if (target == GL_TEXTURE_2D) {
    307         glBindTexture(GL_TEXTURE_2D, texture.name);
    308         glEnable(GL_TEXTURE_2D);
    309 #if defined(GL_OES_EGL_image_external)
    310         if (GLExtensions::getInstance().haveTextureExternal()) {
    311             glDisable(GL_TEXTURE_EXTERNAL_OES);
    312         }
    313     } else {
    314         glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture.name);
    315         glEnable(GL_TEXTURE_EXTERNAL_OES);
    316         glDisable(GL_TEXTURE_2D);
    317 #endif
    318     }
    319 
    320     if (filter) {
    321         glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    322         glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    323     } else {
    324         glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    325         glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    326     }
    327 }
    328 
    329 void TextureManager::deactivateTextures()
    330 {
    331     glDisable(GL_TEXTURE_2D);
    332 #if defined(GL_OES_EGL_image_external)
    333     if (GLExtensions::getInstance().haveTextureExternal()) {
    334         glDisable(GL_TEXTURE_EXTERNAL_OES);
    335     }
    336 #endif
    337 }
    338 
    339 // ---------------------------------------------------------------------------
    340 
    341 }; // namespace android
    342