Texture.cpp revision 9fe7e16399aa9739b63ce9add1d04fd8ef00678f
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/TraceUtils.h" 21 22#include <utils/Log.h> 23 24#include <SkCanvas.h> 25 26namespace android { 27namespace uirenderer { 28 29// Number of bytes used by a texture in the given format 30static int bytesPerPixel(GLint glFormat) { 31 switch (glFormat) { 32 // The wrapped-texture case, usually means a SurfaceTexture 33 case 0: 34 return 0; 35 case GL_LUMINANCE: 36 case GL_ALPHA: 37 return 1; 38 case GL_SRGB8: 39 case GL_RGB: 40 return 3; 41 case GL_SRGB8_ALPHA8: 42 case GL_RGBA: 43 return 4; 44 case GL_RGBA16F: 45 return 8; 46 default: 47 LOG_ALWAYS_FATAL("UNKNOWN FORMAT 0x%x", glFormat); 48 } 49} 50 51void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force) { 52 53 if (force || wrapS != mWrapS || wrapT != mWrapT) { 54 mWrapS = wrapS; 55 mWrapT = wrapT; 56 57 if (bindTexture) { 58 mCaches.textureState().bindTexture(mTarget, mId); 59 } 60 61 glTexParameteri(mTarget, GL_TEXTURE_WRAP_S, wrapS); 62 glTexParameteri(mTarget, GL_TEXTURE_WRAP_T, wrapT); 63 } 64} 65 66void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force) { 67 if (force || min != mMinFilter || mag != mMagFilter) { 68 mMinFilter = min; 69 mMagFilter = mag; 70 71 if (bindTexture) { 72 mCaches.textureState().bindTexture(mTarget, mId); 73 } 74 75 if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR; 76 77 glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, min); 78 glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, mag); 79 } 80} 81 82void Texture::deleteTexture() { 83 mCaches.textureState().deleteTexture(mId); 84 mId = 0; 85 mTarget = GL_NONE; 86 if (mEglImageHandle != EGL_NO_IMAGE_KHR) { 87 EGLDisplay eglDisplayHandle = eglGetCurrentDisplay(); 88 eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle); 89 mEglImageHandle = EGL_NO_IMAGE_KHR; 90 } 91} 92 93bool Texture::updateSize(uint32_t width, uint32_t height, GLint internalFormat, 94 GLint format, GLenum target) { 95 if (mWidth == width 96 && mHeight == height 97 && mFormat == format 98 && mInternalFormat == internalFormat 99 && mTarget == target) { 100 return false; 101 } 102 mWidth = width; 103 mHeight = height; 104 mFormat = format; 105 mInternalFormat = internalFormat; 106 mTarget = target; 107 notifySizeChanged(mWidth * mHeight * bytesPerPixel(internalFormat)); 108 return true; 109} 110 111void Texture::resetCachedParams() { 112 mWrapS = GL_REPEAT; 113 mWrapT = GL_REPEAT; 114 mMinFilter = GL_NEAREST_MIPMAP_LINEAR; 115 mMagFilter = GL_LINEAR; 116} 117 118void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height, 119 GLenum format, GLenum type, const void* pixels) { 120 GL_CHECKPOINT(MODERATE); 121 bool needsAlloc = updateSize(width, height, internalFormat, format, GL_TEXTURE_2D); 122 if (!mId) { 123 glGenTextures(1, &mId); 124 needsAlloc = true; 125 resetCachedParams(); 126 } 127 mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId); 128 if (needsAlloc) { 129 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, 130 format, type, pixels); 131 } else if (pixels) { 132 glTexSubImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, 133 format, type, pixels); 134 } 135 GL_CHECKPOINT(MODERATE); 136} 137 138void Texture::uploadHardwareBitmapToTexture(GraphicBuffer* buffer) { 139 EGLDisplay eglDisplayHandle = eglGetCurrentDisplay(); 140 if (mEglImageHandle != EGL_NO_IMAGE_KHR) { 141 eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle); 142 mEglImageHandle = EGL_NO_IMAGE_KHR; 143 } 144 mEglImageHandle = eglCreateImageKHR(eglDisplayHandle, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, 145 buffer->getNativeBuffer(), 0); 146 glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, mEglImageHandle); 147} 148 149static void uploadToTexture(bool resize, GLint internalFormat, GLenum format, GLenum type, 150 GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, const GLvoid * data) { 151 152 const bool useStride = stride != width 153 && Caches::getInstance().extensions().hasUnpackRowLength(); 154 if ((stride == width) || useStride) { 155 if (useStride) { 156 glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); 157 } 158 159 if (resize) { 160 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, data); 161 } else { 162 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data); 163 } 164 165 if (useStride) { 166 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 167 } 168 } else { 169 // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer 170 // if the stride doesn't match the width 171 172 GLvoid * temp = (GLvoid *) malloc(width * height * bpp); 173 if (!temp) return; 174 175 uint8_t * pDst = (uint8_t *)temp; 176 uint8_t * pSrc = (uint8_t *)data; 177 for (GLsizei i = 0; i < height; i++) { 178 memcpy(pDst, pSrc, width * bpp); 179 pDst += width * bpp; 180 pSrc += stride * bpp; 181 } 182 183 if (resize) { 184 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, temp); 185 } else { 186 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp); 187 } 188 189 free(temp); 190 } 191} 192 193void Texture::colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType, 194 bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType) { 195 switch (colorType) { 196 case kAlpha_8_SkColorType: 197 *outFormat = GL_ALPHA; 198 *outInternalFormat = GL_ALPHA; 199 *outType = GL_UNSIGNED_BYTE; 200 break; 201 case kRGB_565_SkColorType: 202 if (needSRGB) { 203 // We would ideally use a GL_RGB/GL_SRGB8 texture but the 204 // intermediate Skia bitmap needs to be ARGB_8888 205 *outFormat = GL_RGBA; 206 *outInternalFormat = caches.rgbaInternalFormat(); 207 *outType = GL_UNSIGNED_BYTE; 208 } else { 209 *outFormat = GL_RGB; 210 *outInternalFormat = GL_RGB; 211 *outType = GL_UNSIGNED_SHORT_5_6_5; 212 } 213 break; 214 // ARGB_4444 and Index_8 are both upconverted to RGBA_8888 215 case kARGB_4444_SkColorType: 216 case kIndex_8_SkColorType: 217 case kN32_SkColorType: 218 *outFormat = GL_RGBA; 219 *outInternalFormat = caches.rgbaInternalFormat(needSRGB); 220 *outType = GL_UNSIGNED_BYTE; 221 break; 222 case kGray_8_SkColorType: 223 // TODO: Handle sRGB 224 *outFormat = GL_LUMINANCE; 225 *outInternalFormat = GL_LUMINANCE; 226 *outType = GL_UNSIGNED_BYTE; 227 break; 228 case kRGBA_F16_SkColorType: 229 // This format is always linear 230 *outFormat = GL_RGBA; 231 *outInternalFormat = GL_RGBA16F; 232 *outType = GL_HALF_FLOAT; 233 break; 234 default: 235 LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType); 236 break; 237 } 238} 239 240SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasSRGB, sk_sp<SkColorSpace> sRGB) { 241 SkBitmap rgbaBitmap; 242 rgbaBitmap.allocPixels(SkImageInfo::MakeN32(bitmap.width(), bitmap.height(), 243 bitmap.info().alphaType(), hasSRGB ? sRGB : nullptr)); 244 rgbaBitmap.eraseColor(0); 245 SkCanvas canvas(rgbaBitmap); 246 canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr); 247 return rgbaBitmap; 248} 249 250bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasSRGB, SkColorSpace* sRGB) { 251 bool needSRGB = info.colorSpace() == sRGB; 252 return info.colorType() == kARGB_4444_SkColorType 253 || info.colorType() == kIndex_8_SkColorType 254 || (info.colorType() == kRGB_565_SkColorType && hasSRGB && needSRGB); 255} 256 257 258void Texture::upload(Bitmap& bitmap) { 259 if (!bitmap.readyToDraw()) { 260 ALOGE("Cannot generate texture from bitmap"); 261 return; 262 } 263 264 ATRACE_FORMAT("Upload %ux%u Texture", bitmap.width(), bitmap.height()); 265 266 // We could also enable mipmapping if both bitmap dimensions are powers 267 // of 2 but we'd have to deal with size changes. Let's keep this simple 268 const bool canMipMap = mCaches.extensions().hasNPot(); 269 270 // If the texture had mipmap enabled but not anymore, 271 // force a glTexImage2D to discard the mipmap levels 272 bool needsAlloc = canMipMap && mipMap && !bitmap.hasHardwareMipMap(); 273 bool setDefaultParams = false; 274 275 if (!mId) { 276 glGenTextures(1, &mId); 277 needsAlloc = true; 278 setDefaultParams = true; 279 } 280 281 sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named); 282 bool needSRGB = bitmap.info().colorSpace() == sRGB.get(); 283 284 GLint internalFormat, format, type; 285 colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB, &internalFormat, &format, &type); 286 287 GLenum target = bitmap.isHardware() ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; 288 needsAlloc |= updateSize(bitmap.width(), bitmap.height(), internalFormat, format, target); 289 290 blend = !bitmap.isOpaque(); 291 mCaches.textureState().bindTexture(mTarget, mId); 292 293 // TODO: Handle sRGB gray bitmaps 294 bool hasSRGB = mCaches.extensions().hasSRGB(); 295 if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasSRGB, sRGB.get()))) { 296 SkBitmap skBitmap; 297 bitmap.getSkBitmap(&skBitmap); 298 SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasSRGB, std::move(sRGB)); 299 uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(), 300 rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(), 301 rgbaBitmap.height(), rgbaBitmap.getPixels()); 302 } else if (bitmap.isHardware()) { 303 uploadHardwareBitmapToTexture(bitmap.graphicBuffer()); 304 } else { 305 uploadToTexture(needsAlloc, internalFormat, format, type, bitmap.rowBytesAsPixels(), 306 bitmap.info().bytesPerPixel(), bitmap.width(), bitmap.height(), bitmap.pixels()); 307 } 308 309 if (canMipMap) { 310 mipMap = bitmap.hasHardwareMipMap(); 311 if (mipMap) { 312 glGenerateMipmap(GL_TEXTURE_2D); 313 } 314 } 315 316 if (setDefaultParams) { 317 setFilter(GL_NEAREST); 318 setWrap(GL_CLAMP_TO_EDGE); 319 } 320} 321 322void Texture::wrap(GLuint id, uint32_t width, uint32_t height, 323 GLint internalFormat, GLint format, GLenum target) { 324 mId = id; 325 mWidth = width; 326 mHeight = height; 327 mFormat = format; 328 mInternalFormat = internalFormat; 329 mTarget = target; 330 // We're wrapping an existing texture, so don't double count this memory 331 notifySizeChanged(0); 332} 333 334}; // namespace uirenderer 335}; // namespace android 336