Home | History | Annotate | Download | only in hwui
      1 /*
      2  * Copyright (C) 2013 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 "Caches.h"
     18 #include "Texture.h"
     19 #include "utils/GLUtils.h"
     20 #include "utils/MathUtils.h"
     21 #include "utils/TraceUtils.h"
     22 
     23 #include <utils/Log.h>
     24 
     25 #include <math/mat4.h>
     26 
     27 #include <SkCanvas.h>
     28 
     29 namespace android {
     30 namespace uirenderer {
     31 
     32 // Number of bytes used by a texture in the given format
     33 static int bytesPerPixel(GLint glFormat) {
     34     switch (glFormat) {
     35     // The wrapped-texture case, usually means a SurfaceTexture
     36     case 0:
     37         return 0;
     38     case GL_LUMINANCE:
     39     case GL_ALPHA:
     40         return 1;
     41     case GL_SRGB8:
     42     case GL_RGB:
     43         return 3;
     44     case GL_SRGB8_ALPHA8:
     45     case GL_RGBA:
     46         return 4;
     47     case GL_RGBA16F:
     48         return 8;
     49     default:
     50         LOG_ALWAYS_FATAL("UNKNOWN FORMAT 0x%x", glFormat);
     51     }
     52 }
     53 
     54 void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force) {
     55     if (force || wrapS != mWrapS || wrapT != mWrapT) {
     56         mWrapS = wrapS;
     57         mWrapT = wrapT;
     58 
     59         if (bindTexture) {
     60             mCaches.textureState().bindTexture(mTarget, mId);
     61         }
     62 
     63         glTexParameteri(mTarget, GL_TEXTURE_WRAP_S, wrapS);
     64         glTexParameteri(mTarget, GL_TEXTURE_WRAP_T, wrapT);
     65     }
     66 }
     67 
     68 void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force) {
     69     if (force || min != mMinFilter || mag != mMagFilter) {
     70         mMinFilter = min;
     71         mMagFilter = mag;
     72 
     73         if (bindTexture) {
     74             mCaches.textureState().bindTexture(mTarget, mId);
     75         }
     76 
     77         if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR;
     78 
     79         glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, min);
     80         glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, mag);
     81     }
     82 }
     83 
     84 void Texture::deleteTexture() {
     85     mCaches.textureState().deleteTexture(mId);
     86     mId = 0;
     87     mTarget = GL_NONE;
     88     if (mEglImageHandle != EGL_NO_IMAGE_KHR) {
     89         EGLDisplay eglDisplayHandle = eglGetCurrentDisplay();
     90         eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle);
     91         mEglImageHandle = EGL_NO_IMAGE_KHR;
     92     }
     93 }
     94 
     95 bool Texture::updateLayout(uint32_t width, uint32_t height, GLint internalFormat,
     96         GLint format, GLenum target) {
     97     if (mWidth == width
     98             && mHeight == height
     99             && mFormat == format
    100             && mInternalFormat == internalFormat
    101             && mTarget == target) {
    102         return false;
    103     }
    104     mWidth = width;
    105     mHeight = height;
    106     mFormat = format;
    107     mInternalFormat = internalFormat;
    108     mTarget = target;
    109     notifySizeChanged(mWidth * mHeight * bytesPerPixel(internalFormat));
    110     return true;
    111 }
    112 
    113 void Texture::resetCachedParams() {
    114     mWrapS = GL_REPEAT;
    115     mWrapT = GL_REPEAT;
    116     mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
    117     mMagFilter = GL_LINEAR;
    118 }
    119 
    120 void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height,
    121         GLenum format, GLenum type, const void* pixels) {
    122     GL_CHECKPOINT(MODERATE);
    123 
    124     // We don't have color space information, we assume the data is gamma encoded
    125     mIsLinear = false;
    126 
    127     bool needsAlloc = updateLayout(width, height, internalFormat, format, GL_TEXTURE_2D);
    128     if (!mId) {
    129         glGenTextures(1, &mId);
    130         needsAlloc = true;
    131         resetCachedParams();
    132     }
    133     mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId);
    134     if (needsAlloc) {
    135         glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0,
    136                 format, type, pixels);
    137     } else if (pixels) {
    138         glTexSubImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0,
    139                 format, type, pixels);
    140     }
    141     GL_CHECKPOINT(MODERATE);
    142 }
    143 
    144 void Texture::uploadHardwareBitmapToTexture(GraphicBuffer* buffer) {
    145     EGLDisplay eglDisplayHandle = eglGetCurrentDisplay();
    146     if (mEglImageHandle != EGL_NO_IMAGE_KHR) {
    147         eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle);
    148         mEglImageHandle = EGL_NO_IMAGE_KHR;
    149     }
    150     mEglImageHandle = eglCreateImageKHR(eglDisplayHandle, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
    151             buffer->getNativeBuffer(), 0);
    152     glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, mEglImageHandle);
    153 }
    154 
    155 static void uploadToTexture(bool resize, GLint internalFormat, GLenum format, GLenum type,
    156         GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, const GLvoid * data) {
    157 
    158     const bool useStride = stride != width
    159             && Caches::getInstance().extensions().hasUnpackRowLength();
    160     if ((stride == width) || useStride) {
    161         if (useStride) {
    162             glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
    163         }
    164 
    165         if (resize) {
    166             glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, data);
    167         } else {
    168             glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
    169         }
    170 
    171         if (useStride) {
    172             glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
    173         }
    174     } else {
    175         //  With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
    176         //  if the stride doesn't match the width
    177 
    178         GLvoid * temp = (GLvoid *) malloc(width * height * bpp);
    179         if (!temp) return;
    180 
    181         uint8_t * pDst = (uint8_t *)temp;
    182         uint8_t * pSrc = (uint8_t *)data;
    183         for (GLsizei i = 0; i < height; i++) {
    184             memcpy(pDst, pSrc, width * bpp);
    185             pDst += width * bpp;
    186             pSrc += stride * bpp;
    187         }
    188 
    189         if (resize) {
    190             glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, temp);
    191         } else {
    192             glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp);
    193         }
    194 
    195         free(temp);
    196     }
    197 }
    198 
    199 void Texture::colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType,
    200         bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType) {
    201     switch (colorType) {
    202     case kAlpha_8_SkColorType:
    203         *outFormat = GL_ALPHA;
    204         *outInternalFormat = GL_ALPHA;
    205         *outType = GL_UNSIGNED_BYTE;
    206         break;
    207     case kRGB_565_SkColorType:
    208         if (needSRGB) {
    209             // We would ideally use a GL_RGB/GL_SRGB8 texture but the
    210             // intermediate Skia bitmap needs to be ARGB_8888
    211             *outFormat = GL_RGBA;
    212             *outInternalFormat = caches.rgbaInternalFormat();
    213             *outType = GL_UNSIGNED_BYTE;
    214         } else {
    215             *outFormat = GL_RGB;
    216             *outInternalFormat = GL_RGB;
    217             *outType = GL_UNSIGNED_SHORT_5_6_5;
    218         }
    219         break;
    220     // ARGB_4444 is upconverted to RGBA_8888
    221     case kARGB_4444_SkColorType:
    222     case kN32_SkColorType:
    223         *outFormat = GL_RGBA;
    224         *outInternalFormat = caches.rgbaInternalFormat(needSRGB);
    225         *outType = GL_UNSIGNED_BYTE;
    226         break;
    227     case kGray_8_SkColorType:
    228         *outFormat = GL_LUMINANCE;
    229         *outInternalFormat = GL_LUMINANCE;
    230         *outType = GL_UNSIGNED_BYTE;
    231         break;
    232     case kRGBA_F16_SkColorType:
    233         if (caches.extensions().getMajorGlVersion() >= 3) {
    234             // This format is always linear
    235             *outFormat = GL_RGBA;
    236             *outInternalFormat = GL_RGBA16F;
    237             *outType = GL_HALF_FLOAT;
    238         } else {
    239             *outFormat = GL_RGBA;
    240             *outInternalFormat = caches.rgbaInternalFormat(true);
    241             *outType = GL_UNSIGNED_BYTE;
    242         }
    243         break;
    244     default:
    245         LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType);
    246         break;
    247     }
    248 }
    249 
    250 SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending,
    251         sk_sp<SkColorSpace> sRGB) {
    252     SkBitmap rgbaBitmap;
    253     rgbaBitmap.allocPixels(SkImageInfo::MakeN32(bitmap.width(), bitmap.height(),
    254             bitmap.info().alphaType(), hasLinearBlending ? sRGB : nullptr));
    255     rgbaBitmap.eraseColor(0);
    256 
    257     if (bitmap.colorType() == kRGBA_F16_SkColorType) {
    258         // Drawing RGBA_F16 onto ARGB_8888 is not supported
    259         bitmap.readPixels(rgbaBitmap.info()
    260                 .makeColorSpace(SkColorSpace::MakeSRGB()),
    261                 rgbaBitmap.getPixels(), rgbaBitmap.rowBytes(), 0, 0);
    262     } else {
    263         SkCanvas canvas(rgbaBitmap);
    264         canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr);
    265     }
    266 
    267     return rgbaBitmap;
    268 }
    269 
    270 bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending) {
    271     return info.colorType() == kARGB_4444_SkColorType
    272         || (info.colorType() == kRGB_565_SkColorType
    273                 && hasLinearBlending
    274                 && info.colorSpace()->isSRGB())
    275         || (info.colorType() == kRGBA_F16_SkColorType
    276                 && Caches::getInstance().extensions().getMajorGlVersion() < 3);
    277 }
    278 
    279 void Texture::upload(Bitmap& bitmap) {
    280     ATRACE_FORMAT("Upload %ux%u Texture", bitmap.width(), bitmap.height());
    281 
    282     // We could also enable mipmapping if both bitmap dimensions are powers
    283     // of 2 but we'd have to deal with size changes. Let's keep this simple
    284     const bool canMipMap = mCaches.extensions().hasNPot();
    285 
    286     // If the texture had mipmap enabled but not anymore,
    287     // force a glTexImage2D to discard the mipmap levels
    288     bool needsAlloc = canMipMap && mipMap && !bitmap.hasHardwareMipMap();
    289     bool setDefaultParams = false;
    290 
    291     if (!mId) {
    292         glGenTextures(1, &mId);
    293         needsAlloc = true;
    294         setDefaultParams = true;
    295     }
    296 
    297     bool hasLinearBlending = mCaches.extensions().hasLinearBlending();
    298     bool needSRGB = transferFunctionCloseToSRGB(bitmap.info().colorSpace());
    299 
    300     GLint internalFormat, format, type;
    301     colorTypeToGlFormatAndType(mCaches, bitmap.colorType(),
    302             needSRGB && hasLinearBlending, &internalFormat, &format, &type);
    303 
    304     // Some devices don't support GL_RGBA16F, so we need to compare the color type
    305     // and internal GL format to decide what to do with 16 bit bitmaps
    306     bool rgba16fNeedsConversion = bitmap.colorType() == kRGBA_F16_SkColorType
    307             && internalFormat != GL_RGBA16F;
    308 
    309     // RGBA16F is always linear extended sRGB
    310     if (internalFormat == GL_RGBA16F) {
    311         mIsLinear = true;
    312     }
    313 
    314     mConnector.reset();
    315 
    316     // Alpha masks don't have color profiles
    317     // If an RGBA16F bitmap needs conversion, we know the target will be sRGB
    318     if (!mIsLinear && internalFormat != GL_ALPHA && !rgba16fNeedsConversion) {
    319         SkColorSpace* colorSpace = bitmap.info().colorSpace();
    320         // If the bitmap is sRGB we don't need conversion
    321         if (colorSpace != nullptr && !colorSpace->isSRGB()) {
    322             SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor);
    323             if (!colorSpace->toXYZD50(&xyzMatrix)) {
    324                 ALOGW("Incompatible color space!");
    325             } else {
    326                 SkColorSpaceTransferFn fn;
    327                 if (!colorSpace->isNumericalTransferFn(&fn)) {
    328                     ALOGW("Incompatible color space, no numerical transfer function!");
    329                 } else {
    330                     float data[16];
    331                     xyzMatrix.asColMajorf(data);
    332 
    333                     ColorSpace::TransferParameters p =
    334                             {fn.fG, fn.fA, fn.fB, fn.fC, fn.fD, fn.fE, fn.fF};
    335                     ColorSpace src("Unnamed", mat4f((const float*) &data[0]).upperLeft(), p);
    336                     mConnector.reset(new ColorSpaceConnector(src, ColorSpace::sRGB()));
    337 
    338                     // A non-sRGB color space might have a transfer function close enough to sRGB
    339                     // that we can save shader instructions by using an sRGB sampler
    340                     // This is only possible if we have hardware support for sRGB textures
    341                     if (needSRGB && internalFormat == GL_RGBA
    342                             && mCaches.extensions().hasSRGB() && !bitmap.isHardware()) {
    343                         internalFormat = GL_SRGB8_ALPHA8;
    344                     }
    345                 }
    346             }
    347         }
    348     }
    349 
    350     GLenum target = bitmap.isHardware() ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
    351     needsAlloc |= updateLayout(bitmap.width(), bitmap.height(), internalFormat, format, target);
    352 
    353     blend = !bitmap.isOpaque();
    354     mCaches.textureState().bindTexture(mTarget, mId);
    355 
    356     // TODO: Handle sRGB gray bitmaps
    357     if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasLinearBlending))) {
    358         SkBitmap skBitmap;
    359         bitmap.getSkBitmap(&skBitmap);
    360         sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeSRGB();
    361         SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB));
    362         uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(),
    363                 rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(),
    364                 rgbaBitmap.height(), rgbaBitmap.getPixels());
    365     } else if (bitmap.isHardware()) {
    366         uploadHardwareBitmapToTexture(bitmap.graphicBuffer());
    367     } else {
    368         uploadToTexture(needsAlloc, internalFormat, format, type, bitmap.rowBytesAsPixels(),
    369                 bitmap.info().bytesPerPixel(), bitmap.width(), bitmap.height(), bitmap.pixels());
    370     }
    371 
    372     if (canMipMap) {
    373         mipMap = bitmap.hasHardwareMipMap();
    374         if (mipMap) {
    375             glGenerateMipmap(GL_TEXTURE_2D);
    376         }
    377     }
    378 
    379     if (setDefaultParams) {
    380         setFilter(GL_NEAREST);
    381         setWrap(GL_CLAMP_TO_EDGE);
    382     }
    383 }
    384 
    385 void Texture::wrap(GLuint id, uint32_t width, uint32_t height,
    386         GLint internalFormat, GLint format, GLenum target) {
    387     mId = id;
    388     mWidth = width;
    389     mHeight = height;
    390     mFormat = format;
    391     mInternalFormat = internalFormat;
    392     mTarget = target;
    393     mConnector.reset();
    394     // We're wrapping an existing texture, so don't double count this memory
    395     notifySizeChanged(0);
    396 }
    397 
    398 TransferFunctionType Texture::getTransferFunctionType() const {
    399     if (mConnector.get() != nullptr && mInternalFormat != GL_SRGB8_ALPHA8) {
    400         const ColorSpace::TransferParameters& p = mConnector->getSource().getTransferParameters();
    401         if (MathUtils::isZero(p.e) && MathUtils::isZero(p.f)) {
    402             if (MathUtils::areEqual(p.a, 1.0f) && MathUtils::isZero(p.b)
    403                     && MathUtils::isZero(p.c) && MathUtils::isZero(p.d)) {
    404                 if (MathUtils::areEqual(p.g, 1.0f)) {
    405                     return TransferFunctionType::None;
    406                 }
    407                 return TransferFunctionType::Gamma;
    408             }
    409             return TransferFunctionType::Limited;
    410         }
    411         return TransferFunctionType::Full;
    412     }
    413     return TransferFunctionType::None;
    414 }
    415 
    416 }; // namespace uirenderer
    417 }; // namespace android
    418