blob: 0dbd7674e2aad66bd93cd7ecb52a5b442293afb7 [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:
Romain Guy9fe7e162017-02-03 16:16:07 -080047 LOG_ALWAYS_FATAL("UNKNOWN FORMAT 0x%x", glFormat);
John Reck38e0c322015-11-10 12:19:17 -080048 }
49}
50
Romain Guy636afc12017-02-07 11:21:05 -080051bool Texture::isLinear() const {
52 return mInternalFormat == GL_RGBA16F;
53}
54
sergeyv2a38c422016-10-25 15:21:50 -070055void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force) {
Romain Guy8aa195d2013-06-04 18:00:09 -070056
John Reck48247a22016-01-22 10:55:32 -080057 if (force || wrapS != mWrapS || wrapT != mWrapT) {
Romain Guy8aa195d2013-06-04 18:00:09 -070058 mWrapS = wrapS;
59 mWrapT = wrapT;
60
61 if (bindTexture) {
sergeyv2a38c422016-10-25 15:21:50 -070062 mCaches.textureState().bindTexture(mTarget, mId);
Romain Guy8aa195d2013-06-04 18:00:09 -070063 }
64
sergeyv2a38c422016-10-25 15:21:50 -070065 glTexParameteri(mTarget, GL_TEXTURE_WRAP_S, wrapS);
66 glTexParameteri(mTarget, GL_TEXTURE_WRAP_T, wrapT);
Romain Guy8aa195d2013-06-04 18:00:09 -070067 }
68}
69
sergeyv2a38c422016-10-25 15:21:50 -070070void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force) {
John Reck48247a22016-01-22 10:55:32 -080071 if (force || min != mMinFilter || mag != mMagFilter) {
Romain Guy8aa195d2013-06-04 18:00:09 -070072 mMinFilter = min;
73 mMagFilter = mag;
74
75 if (bindTexture) {
sergeyv2a38c422016-10-25 15:21:50 -070076 mCaches.textureState().bindTexture(mTarget, mId);
Romain Guy8aa195d2013-06-04 18:00:09 -070077 }
78
79 if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR;
80
sergeyv2a38c422016-10-25 15:21:50 -070081 glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, min);
82 glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, mag);
Romain Guy8aa195d2013-06-04 18:00:09 -070083 }
84}
85
John Reck38e0c322015-11-10 12:19:17 -080086void Texture::deleteTexture() {
87 mCaches.textureState().deleteTexture(mId);
88 mId = 0;
sergeyv694d4992016-10-27 10:23:13 -070089 mTarget = GL_NONE;
90 if (mEglImageHandle != EGL_NO_IMAGE_KHR) {
91 EGLDisplay eglDisplayHandle = eglGetCurrentDisplay();
92 eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle);
93 mEglImageHandle = EGL_NO_IMAGE_KHR;
94 }
John Reck38e0c322015-11-10 12:19:17 -080095}
96
sergeyv2a38c422016-10-25 15:21:50 -070097bool Texture::updateSize(uint32_t width, uint32_t height, GLint internalFormat,
98 GLint format, GLenum target) {
99 if (mWidth == width
100 && mHeight == height
101 && mFormat == format
102 && mInternalFormat == internalFormat
103 && mTarget == target) {
John Reck38e0c322015-11-10 12:19:17 -0800104 return false;
105 }
106 mWidth = width;
107 mHeight = height;
108 mFormat = format;
Romain Guy253f2c22016-09-28 17:34:42 -0700109 mInternalFormat = internalFormat;
sergeyv2a38c422016-10-25 15:21:50 -0700110 mTarget = target;
Romain Guy253f2c22016-09-28 17:34:42 -0700111 notifySizeChanged(mWidth * mHeight * bytesPerPixel(internalFormat));
John Reck38e0c322015-11-10 12:19:17 -0800112 return true;
113}
114
John Reck48247a22016-01-22 10:55:32 -0800115void Texture::resetCachedParams() {
116 mWrapS = GL_REPEAT;
117 mWrapT = GL_REPEAT;
118 mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
119 mMagFilter = GL_LINEAR;
120}
121
Romain Guy253f2c22016-09-28 17:34:42 -0700122void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height,
John Reck38e0c322015-11-10 12:19:17 -0800123 GLenum format, GLenum type, const void* pixels) {
John Reck975591a2016-01-22 16:28:07 -0800124 GL_CHECKPOINT(MODERATE);
sergeyv2a38c422016-10-25 15:21:50 -0700125 bool needsAlloc = updateSize(width, height, internalFormat, format, GL_TEXTURE_2D);
John Reck38e0c322015-11-10 12:19:17 -0800126 if (!mId) {
127 glGenTextures(1, &mId);
128 needsAlloc = true;
John Reck48247a22016-01-22 10:55:32 -0800129 resetCachedParams();
John Reck38e0c322015-11-10 12:19:17 -0800130 }
131 mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId);
132 if (needsAlloc) {
Romain Guy253f2c22016-09-28 17:34:42 -0700133 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0,
John Reck38e0c322015-11-10 12:19:17 -0800134 format, type, pixels);
John Reck66f65cb2016-01-21 09:08:42 -0800135 } else if (pixels) {
Romain Guy253f2c22016-09-28 17:34:42 -0700136 glTexSubImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0,
John Reck38e0c322015-11-10 12:19:17 -0800137 format, type, pixels);
138 }
John Reck975591a2016-01-22 16:28:07 -0800139 GL_CHECKPOINT(MODERATE);
John Reck38e0c322015-11-10 12:19:17 -0800140}
141
sergeyv694d4992016-10-27 10:23:13 -0700142void Texture::uploadHardwareBitmapToTexture(GraphicBuffer* buffer) {
143 EGLDisplay eglDisplayHandle = eglGetCurrentDisplay();
144 if (mEglImageHandle != EGL_NO_IMAGE_KHR) {
145 eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle);
146 mEglImageHandle = EGL_NO_IMAGE_KHR;
147 }
148 mEglImageHandle = eglCreateImageKHR(eglDisplayHandle, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
149 buffer->getNativeBuffer(), 0);
150 glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, mEglImageHandle);
151}
152
Romain Guy253f2c22016-09-28 17:34:42 -0700153static void uploadToTexture(bool resize, GLint internalFormat, GLenum format, GLenum type,
154 GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, const GLvoid * data) {
John Reck38e0c322015-11-10 12:19:17 -0800155
John Reck38e0c322015-11-10 12:19:17 -0800156 const bool useStride = stride != width
157 && Caches::getInstance().extensions().hasUnpackRowLength();
158 if ((stride == width) || useStride) {
159 if (useStride) {
160 glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
161 }
162
163 if (resize) {
Romain Guy253f2c22016-09-28 17:34:42 -0700164 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, data);
John Reck38e0c322015-11-10 12:19:17 -0800165 } else {
166 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
167 }
168
169 if (useStride) {
170 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
171 }
172 } else {
173 // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
174 // if the stride doesn't match the width
175
176 GLvoid * temp = (GLvoid *) malloc(width * height * bpp);
177 if (!temp) return;
178
179 uint8_t * pDst = (uint8_t *)temp;
180 uint8_t * pSrc = (uint8_t *)data;
181 for (GLsizei i = 0; i < height; i++) {
182 memcpy(pDst, pSrc, width * bpp);
183 pDst += width * bpp;
184 pSrc += stride * bpp;
185 }
186
187 if (resize) {
Romain Guy253f2c22016-09-28 17:34:42 -0700188 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, temp);
John Reck38e0c322015-11-10 12:19:17 -0800189 } else {
190 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp);
191 }
192
193 free(temp);
194 }
195}
196
sergeyv694d4992016-10-27 10:23:13 -0700197void Texture::colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType,
Romain Guy253f2c22016-09-28 17:34:42 -0700198 bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType) {
John Reck38e0c322015-11-10 12:19:17 -0800199 switch (colorType) {
200 case kAlpha_8_SkColorType:
201 *outFormat = GL_ALPHA;
Romain Guy253f2c22016-09-28 17:34:42 -0700202 *outInternalFormat = GL_ALPHA;
John Reck38e0c322015-11-10 12:19:17 -0800203 *outType = GL_UNSIGNED_BYTE;
204 break;
205 case kRGB_565_SkColorType:
Romain Guy253f2c22016-09-28 17:34:42 -0700206 if (needSRGB) {
207 // We would ideally use a GL_RGB/GL_SRGB8 texture but the
208 // intermediate Skia bitmap needs to be ARGB_8888
209 *outFormat = GL_RGBA;
210 *outInternalFormat = caches.rgbaInternalFormat();
211 *outType = GL_UNSIGNED_BYTE;
212 } else {
213 *outFormat = GL_RGB;
214 *outInternalFormat = GL_RGB;
215 *outType = GL_UNSIGNED_SHORT_5_6_5;
216 }
John Reck38e0c322015-11-10 12:19:17 -0800217 break;
218 // ARGB_4444 and Index_8 are both upconverted to RGBA_8888
219 case kARGB_4444_SkColorType:
220 case kIndex_8_SkColorType:
221 case kN32_SkColorType:
222 *outFormat = GL_RGBA;
Romain Guy253f2c22016-09-28 17:34:42 -0700223 *outInternalFormat = caches.rgbaInternalFormat(needSRGB);
John Reck38e0c322015-11-10 12:19:17 -0800224 *outType = GL_UNSIGNED_BYTE;
225 break;
Derek Sollenberger88d842f2016-01-20 10:37:30 -0500226 case kGray_8_SkColorType:
Romain Guy253f2c22016-09-28 17:34:42 -0700227 // TODO: Handle sRGB
Derek Sollenberger88d842f2016-01-20 10:37:30 -0500228 *outFormat = GL_LUMINANCE;
Romain Guy253f2c22016-09-28 17:34:42 -0700229 *outInternalFormat = GL_LUMINANCE;
Derek Sollenberger88d842f2016-01-20 10:37:30 -0500230 *outType = GL_UNSIGNED_BYTE;
231 break;
Romain Guy9505a652016-12-14 09:43:50 -0800232 case kRGBA_F16_SkColorType:
233 // This format is always linear
234 *outFormat = GL_RGBA;
235 *outInternalFormat = GL_RGBA16F;
236 *outType = GL_HALF_FLOAT;
237 break;
John Reck38e0c322015-11-10 12:19:17 -0800238 default:
239 LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType);
240 break;
241 }
242}
243
sergeyv694d4992016-10-27 10:23:13 -0700244SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasSRGB, sk_sp<SkColorSpace> sRGB) {
245 SkBitmap rgbaBitmap;
246 rgbaBitmap.allocPixels(SkImageInfo::MakeN32(bitmap.width(), bitmap.height(),
247 bitmap.info().alphaType(), hasSRGB ? sRGB : nullptr));
248 rgbaBitmap.eraseColor(0);
249 SkCanvas canvas(rgbaBitmap);
250 canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr);
251 return rgbaBitmap;
252}
253
254bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasSRGB, SkColorSpace* sRGB) {
255 bool needSRGB = info.colorSpace() == sRGB;
256 return info.colorType() == kARGB_4444_SkColorType
257 || info.colorType() == kIndex_8_SkColorType
258 || (info.colorType() == kRGB_565_SkColorType && hasSRGB && needSRGB);
259}
260
261
sergeyv98fa4f92016-10-24 15:35:21 -0700262void Texture::upload(Bitmap& bitmap) {
John Reck38e0c322015-11-10 12:19:17 -0800263 if (!bitmap.readyToDraw()) {
264 ALOGE("Cannot generate texture from bitmap");
265 return;
266 }
267
268 ATRACE_FORMAT("Upload %ux%u Texture", bitmap.width(), bitmap.height());
269
270 // We could also enable mipmapping if both bitmap dimensions are powers
271 // of 2 but we'd have to deal with size changes. Let's keep this simple
272 const bool canMipMap = mCaches.extensions().hasNPot();
273
274 // If the texture had mipmap enabled but not anymore,
275 // force a glTexImage2D to discard the mipmap levels
276 bool needsAlloc = canMipMap && mipMap && !bitmap.hasHardwareMipMap();
John Reck48247a22016-01-22 10:55:32 -0800277 bool setDefaultParams = false;
John Reck38e0c322015-11-10 12:19:17 -0800278
279 if (!mId) {
280 glGenTextures(1, &mId);
281 needsAlloc = true;
John Reck48247a22016-01-22 10:55:32 -0800282 setDefaultParams = true;
John Reck38e0c322015-11-10 12:19:17 -0800283 }
284
Matt Sarett89ddb1f2017-02-10 13:31:56 -0500285 sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeSRGB();
sergeyv98fa4f92016-10-24 15:35:21 -0700286 bool needSRGB = bitmap.info().colorSpace() == sRGB.get();
John Reck38e0c322015-11-10 12:19:17 -0800287
Romain Guy253f2c22016-09-28 17:34:42 -0700288 GLint internalFormat, format, type;
289 colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB, &internalFormat, &format, &type);
290
sergeyv694d4992016-10-27 10:23:13 -0700291 GLenum target = bitmap.isHardware() ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
292 needsAlloc |= updateSize(bitmap.width(), bitmap.height(), internalFormat, format, target);
John Reck38e0c322015-11-10 12:19:17 -0800293
294 blend = !bitmap.isOpaque();
sergeyv694d4992016-10-27 10:23:13 -0700295 mCaches.textureState().bindTexture(mTarget, mId);
John Reck38e0c322015-11-10 12:19:17 -0800296
Romain Guy253f2c22016-09-28 17:34:42 -0700297 // TODO: Handle sRGB gray bitmaps
298 bool hasSRGB = mCaches.extensions().hasSRGB();
sergeyv694d4992016-10-27 10:23:13 -0700299 if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasSRGB, sRGB.get()))) {
sergeyv98fa4f92016-10-24 15:35:21 -0700300 SkBitmap skBitmap;
301 bitmap.getSkBitmap(&skBitmap);
sergeyv694d4992016-10-27 10:23:13 -0700302 SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasSRGB, std::move(sRGB));
sergeyv98fa4f92016-10-24 15:35:21 -0700303 uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(),
304 rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(),
305 rgbaBitmap.height(), rgbaBitmap.getPixels());
sergeyv694d4992016-10-27 10:23:13 -0700306 } else if (bitmap.isHardware()) {
307 uploadHardwareBitmapToTexture(bitmap.graphicBuffer());
John Reck38e0c322015-11-10 12:19:17 -0800308 } else {
sergeyv98fa4f92016-10-24 15:35:21 -0700309 uploadToTexture(needsAlloc, internalFormat, format, type, bitmap.rowBytesAsPixels(),
310 bitmap.info().bytesPerPixel(), bitmap.width(), bitmap.height(), bitmap.pixels());
John Reck38e0c322015-11-10 12:19:17 -0800311 }
312
313 if (canMipMap) {
314 mipMap = bitmap.hasHardwareMipMap();
315 if (mipMap) {
316 glGenerateMipmap(GL_TEXTURE_2D);
317 }
318 }
319
John Reck48247a22016-01-22 10:55:32 -0800320 if (setDefaultParams) {
John Reck38e0c322015-11-10 12:19:17 -0800321 setFilter(GL_NEAREST);
John Reck38e0c322015-11-10 12:19:17 -0800322 setWrap(GL_CLAMP_TO_EDGE);
323 }
324}
325
sergeyv2a38c422016-10-25 15:21:50 -0700326void Texture::wrap(GLuint id, uint32_t width, uint32_t height,
327 GLint internalFormat, GLint format, GLenum target) {
John Reck38e0c322015-11-10 12:19:17 -0800328 mId = id;
329 mWidth = width;
330 mHeight = height;
331 mFormat = format;
Romain Guy253f2c22016-09-28 17:34:42 -0700332 mInternalFormat = internalFormat;
sergeyv2a38c422016-10-25 15:21:50 -0700333 mTarget = target;
John Reck38e0c322015-11-10 12:19:17 -0800334 // We're wrapping an existing texture, so don't double count this memory
335 notifySizeChanged(0);
Romain Guybe1b1272013-06-06 14:02:54 -0700336}
337
Romain Guy8aa195d2013-06-04 18:00:09 -0700338}; // namespace uirenderer
339}; // namespace android