blob: 5b5b74e1c3f3f1fc6aaade40a9a2dd948292cc7b [file] [log] [blame]
Romain Guy8aa195d2013-06-04 18:00:09 -07001/*
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
Romain Guy8aa195d2013-06-04 18:00:09 -070017#include "Caches.h"
18#include "Texture.h"
John Reck9372ac32016-01-19 11:46:52 -080019#include "utils/GLUtils.h"
John Reck38e0c322015-11-10 12:19:17 -080020#include "utils/TraceUtils.h"
21
22#include <utils/Log.h>
23
24#include <SkCanvas.h>
Romain Guy8aa195d2013-06-04 18:00:09 -070025
26namespace android {
27namespace uirenderer {
28
Romain Guy253f2c22016-09-28 17:34:42 -070029// Number of bytes used by a texture in the given format
John Reck38e0c322015-11-10 12:19:17 -080030static int bytesPerPixel(GLint glFormat) {
31 switch (glFormat) {
John Reck623d2232016-02-12 08:08:29 -080032 // The wrapped-texture case, usually means a SurfaceTexture
33 case 0:
34 return 0;
Romain Guy253f2c22016-09-28 17:34:42 -070035 case GL_LUMINANCE:
John Reck38e0c322015-11-10 12:19:17 -080036 case GL_ALPHA:
37 return 1;
Romain Guy253f2c22016-09-28 17:34:42 -070038 case GL_SRGB8:
John Reck38e0c322015-11-10 12:19:17 -080039 case GL_RGB:
40 return 3;
Romain Guy253f2c22016-09-28 17:34:42 -070041 case GL_SRGB8_ALPHA8:
John Reck38e0c322015-11-10 12:19:17 -080042 case GL_RGBA:
John Reck38e0c322015-11-10 12:19:17 -080043 return 4;
John Reck1d4e6a02016-02-11 13:22:25 -080044 case GL_RGBA16F:
Romain Guy253f2c22016-09-28 17:34:42 -070045 return 8;
John Reck1d4e6a02016-02-11 13:22:25 -080046 default:
47 LOG_ALWAYS_FATAL("UNKNOWN FORMAT %d", glFormat);
John Reck38e0c322015-11-10 12:19:17 -080048 }
49}
50
sergeyv2a38c422016-10-25 15:21:50 -070051void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force) {
Romain Guy8aa195d2013-06-04 18:00:09 -070052
John Reck48247a22016-01-22 10:55:32 -080053 if (force || wrapS != mWrapS || wrapT != mWrapT) {
Romain Guy8aa195d2013-06-04 18:00:09 -070054 mWrapS = wrapS;
55 mWrapT = wrapT;
56
57 if (bindTexture) {
sergeyv2a38c422016-10-25 15:21:50 -070058 mCaches.textureState().bindTexture(mTarget, mId);
Romain Guy8aa195d2013-06-04 18:00:09 -070059 }
60
sergeyv2a38c422016-10-25 15:21:50 -070061 glTexParameteri(mTarget, GL_TEXTURE_WRAP_S, wrapS);
62 glTexParameteri(mTarget, GL_TEXTURE_WRAP_T, wrapT);
Romain Guy8aa195d2013-06-04 18:00:09 -070063 }
64}
65
sergeyv2a38c422016-10-25 15:21:50 -070066void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force) {
John Reck48247a22016-01-22 10:55:32 -080067 if (force || min != mMinFilter || mag != mMagFilter) {
Romain Guy8aa195d2013-06-04 18:00:09 -070068 mMinFilter = min;
69 mMagFilter = mag;
70
71 if (bindTexture) {
sergeyv2a38c422016-10-25 15:21:50 -070072 mCaches.textureState().bindTexture(mTarget, mId);
Romain Guy8aa195d2013-06-04 18:00:09 -070073 }
74
75 if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR;
76
sergeyv2a38c422016-10-25 15:21:50 -070077 glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, min);
78 glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, mag);
Romain Guy8aa195d2013-06-04 18:00:09 -070079 }
80}
81
John Reck38e0c322015-11-10 12:19:17 -080082void Texture::deleteTexture() {
83 mCaches.textureState().deleteTexture(mId);
84 mId = 0;
sergeyv694d4992016-10-27 10:23:13 -070085 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 }
John Reck38e0c322015-11-10 12:19:17 -080091}
92
sergeyv2a38c422016-10-25 15:21:50 -070093bool 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) {
John Reck38e0c322015-11-10 12:19:17 -0800100 return false;
101 }
102 mWidth = width;
103 mHeight = height;
104 mFormat = format;
Romain Guy253f2c22016-09-28 17:34:42 -0700105 mInternalFormat = internalFormat;
sergeyv2a38c422016-10-25 15:21:50 -0700106 mTarget = target;
Romain Guy253f2c22016-09-28 17:34:42 -0700107 notifySizeChanged(mWidth * mHeight * bytesPerPixel(internalFormat));
John Reck38e0c322015-11-10 12:19:17 -0800108 return true;
109}
110
John Reck48247a22016-01-22 10:55:32 -0800111void Texture::resetCachedParams() {
112 mWrapS = GL_REPEAT;
113 mWrapT = GL_REPEAT;
114 mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
115 mMagFilter = GL_LINEAR;
116}
117
Romain Guy253f2c22016-09-28 17:34:42 -0700118void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height,
John Reck38e0c322015-11-10 12:19:17 -0800119 GLenum format, GLenum type, const void* pixels) {
John Reck975591a2016-01-22 16:28:07 -0800120 GL_CHECKPOINT(MODERATE);
sergeyv2a38c422016-10-25 15:21:50 -0700121 bool needsAlloc = updateSize(width, height, internalFormat, format, GL_TEXTURE_2D);
John Reck38e0c322015-11-10 12:19:17 -0800122 if (!mId) {
123 glGenTextures(1, &mId);
124 needsAlloc = true;
John Reck48247a22016-01-22 10:55:32 -0800125 resetCachedParams();
John Reck38e0c322015-11-10 12:19:17 -0800126 }
127 mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId);
128 if (needsAlloc) {
Romain Guy253f2c22016-09-28 17:34:42 -0700129 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0,
John Reck38e0c322015-11-10 12:19:17 -0800130 format, type, pixels);
John Reck66f65cb2016-01-21 09:08:42 -0800131 } else if (pixels) {
Romain Guy253f2c22016-09-28 17:34:42 -0700132 glTexSubImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0,
John Reck38e0c322015-11-10 12:19:17 -0800133 format, type, pixels);
134 }
John Reck975591a2016-01-22 16:28:07 -0800135 GL_CHECKPOINT(MODERATE);
John Reck38e0c322015-11-10 12:19:17 -0800136}
137
sergeyv694d4992016-10-27 10:23:13 -0700138void 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
Romain Guy253f2c22016-09-28 17:34:42 -0700149static void uploadToTexture(bool resize, GLint internalFormat, GLenum format, GLenum type,
150 GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, const GLvoid * data) {
John Reck38e0c322015-11-10 12:19:17 -0800151
John Reck38e0c322015-11-10 12:19:17 -0800152 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) {
Romain Guy253f2c22016-09-28 17:34:42 -0700160 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, data);
John Reck38e0c322015-11-10 12:19:17 -0800161 } 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) {
Romain Guy253f2c22016-09-28 17:34:42 -0700184 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, temp);
John Reck38e0c322015-11-10 12:19:17 -0800185 } else {
186 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp);
187 }
188
189 free(temp);
190 }
191}
192
sergeyv694d4992016-10-27 10:23:13 -0700193void Texture::colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType,
Romain Guy253f2c22016-09-28 17:34:42 -0700194 bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType) {
John Reck38e0c322015-11-10 12:19:17 -0800195 switch (colorType) {
196 case kAlpha_8_SkColorType:
197 *outFormat = GL_ALPHA;
Romain Guy253f2c22016-09-28 17:34:42 -0700198 *outInternalFormat = GL_ALPHA;
John Reck38e0c322015-11-10 12:19:17 -0800199 *outType = GL_UNSIGNED_BYTE;
200 break;
201 case kRGB_565_SkColorType:
Romain Guy253f2c22016-09-28 17:34:42 -0700202 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 }
John Reck38e0c322015-11-10 12:19:17 -0800213 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;
Romain Guy253f2c22016-09-28 17:34:42 -0700219 *outInternalFormat = caches.rgbaInternalFormat(needSRGB);
John Reck38e0c322015-11-10 12:19:17 -0800220 *outType = GL_UNSIGNED_BYTE;
221 break;
Derek Sollenberger88d842f2016-01-20 10:37:30 -0500222 case kGray_8_SkColorType:
Romain Guy253f2c22016-09-28 17:34:42 -0700223 // TODO: Handle sRGB
Derek Sollenberger88d842f2016-01-20 10:37:30 -0500224 *outFormat = GL_LUMINANCE;
Romain Guy253f2c22016-09-28 17:34:42 -0700225 *outInternalFormat = GL_LUMINANCE;
Derek Sollenberger88d842f2016-01-20 10:37:30 -0500226 *outType = GL_UNSIGNED_BYTE;
227 break;
John Reck38e0c322015-11-10 12:19:17 -0800228 default:
229 LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType);
230 break;
231 }
232}
233
sergeyv694d4992016-10-27 10:23:13 -0700234SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasSRGB, sk_sp<SkColorSpace> sRGB) {
235 SkBitmap rgbaBitmap;
236 rgbaBitmap.allocPixels(SkImageInfo::MakeN32(bitmap.width(), bitmap.height(),
237 bitmap.info().alphaType(), hasSRGB ? sRGB : nullptr));
238 rgbaBitmap.eraseColor(0);
239 SkCanvas canvas(rgbaBitmap);
240 canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr);
241 return rgbaBitmap;
242}
243
244bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasSRGB, SkColorSpace* sRGB) {
245 bool needSRGB = info.colorSpace() == sRGB;
246 return info.colorType() == kARGB_4444_SkColorType
247 || info.colorType() == kIndex_8_SkColorType
248 || (info.colorType() == kRGB_565_SkColorType && hasSRGB && needSRGB);
249}
250
251
sergeyv98fa4f92016-10-24 15:35:21 -0700252void Texture::upload(Bitmap& bitmap) {
John Reck38e0c322015-11-10 12:19:17 -0800253 if (!bitmap.readyToDraw()) {
254 ALOGE("Cannot generate texture from bitmap");
255 return;
256 }
257
258 ATRACE_FORMAT("Upload %ux%u Texture", bitmap.width(), bitmap.height());
259
260 // We could also enable mipmapping if both bitmap dimensions are powers
261 // of 2 but we'd have to deal with size changes. Let's keep this simple
262 const bool canMipMap = mCaches.extensions().hasNPot();
263
264 // If the texture had mipmap enabled but not anymore,
265 // force a glTexImage2D to discard the mipmap levels
266 bool needsAlloc = canMipMap && mipMap && !bitmap.hasHardwareMipMap();
John Reck48247a22016-01-22 10:55:32 -0800267 bool setDefaultParams = false;
John Reck38e0c322015-11-10 12:19:17 -0800268
269 if (!mId) {
270 glGenTextures(1, &mId);
271 needsAlloc = true;
John Reck48247a22016-01-22 10:55:32 -0800272 setDefaultParams = true;
John Reck38e0c322015-11-10 12:19:17 -0800273 }
274
Mike Reedab12c1f2016-11-03 12:54:10 -0400275 sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
sergeyv98fa4f92016-10-24 15:35:21 -0700276 bool needSRGB = bitmap.info().colorSpace() == sRGB.get();
John Reck38e0c322015-11-10 12:19:17 -0800277
Romain Guy253f2c22016-09-28 17:34:42 -0700278 GLint internalFormat, format, type;
279 colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB, &internalFormat, &format, &type);
280
sergeyv694d4992016-10-27 10:23:13 -0700281 GLenum target = bitmap.isHardware() ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
282 needsAlloc |= updateSize(bitmap.width(), bitmap.height(), internalFormat, format, target);
John Reck38e0c322015-11-10 12:19:17 -0800283
284 blend = !bitmap.isOpaque();
sergeyv694d4992016-10-27 10:23:13 -0700285 mCaches.textureState().bindTexture(mTarget, mId);
John Reck38e0c322015-11-10 12:19:17 -0800286
Romain Guy253f2c22016-09-28 17:34:42 -0700287 // TODO: Handle sRGB gray bitmaps
288 bool hasSRGB = mCaches.extensions().hasSRGB();
sergeyv694d4992016-10-27 10:23:13 -0700289 if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasSRGB, sRGB.get()))) {
sergeyv98fa4f92016-10-24 15:35:21 -0700290 SkBitmap skBitmap;
291 bitmap.getSkBitmap(&skBitmap);
sergeyv694d4992016-10-27 10:23:13 -0700292 SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasSRGB, std::move(sRGB));
sergeyv98fa4f92016-10-24 15:35:21 -0700293 uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(),
294 rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(),
295 rgbaBitmap.height(), rgbaBitmap.getPixels());
sergeyv694d4992016-10-27 10:23:13 -0700296 } else if (bitmap.isHardware()) {
297 uploadHardwareBitmapToTexture(bitmap.graphicBuffer());
John Reck38e0c322015-11-10 12:19:17 -0800298 } else {
sergeyv98fa4f92016-10-24 15:35:21 -0700299 uploadToTexture(needsAlloc, internalFormat, format, type, bitmap.rowBytesAsPixels(),
300 bitmap.info().bytesPerPixel(), bitmap.width(), bitmap.height(), bitmap.pixels());
John Reck38e0c322015-11-10 12:19:17 -0800301 }
302
303 if (canMipMap) {
304 mipMap = bitmap.hasHardwareMipMap();
305 if (mipMap) {
306 glGenerateMipmap(GL_TEXTURE_2D);
307 }
308 }
309
John Reck48247a22016-01-22 10:55:32 -0800310 if (setDefaultParams) {
John Reck38e0c322015-11-10 12:19:17 -0800311 setFilter(GL_NEAREST);
John Reck38e0c322015-11-10 12:19:17 -0800312 setWrap(GL_CLAMP_TO_EDGE);
313 }
314}
315
sergeyv2a38c422016-10-25 15:21:50 -0700316void Texture::wrap(GLuint id, uint32_t width, uint32_t height,
317 GLint internalFormat, GLint format, GLenum target) {
John Reck38e0c322015-11-10 12:19:17 -0800318 mId = id;
319 mWidth = width;
320 mHeight = height;
321 mFormat = format;
Romain Guy253f2c22016-09-28 17:34:42 -0700322 mInternalFormat = internalFormat;
sergeyv2a38c422016-10-25 15:21:50 -0700323 mTarget = target;
John Reck38e0c322015-11-10 12:19:17 -0800324 // We're wrapping an existing texture, so don't double count this memory
325 notifySizeChanged(0);
Romain Guybe1b1272013-06-06 14:02:54 -0700326}
327
Romain Guy8aa195d2013-06-04 18:00:09 -0700328}; // namespace uirenderer
329}; // namespace android